Compare commits
	
		
			39 Commits
		
	
	
		
			web/bug/fi
			...
			version/20
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ad7ad1fa78 | |||
| c70e609e50 | |||
| 5f08485fff | |||
| 3a2ed11821 | |||
| ee04f39e28 | |||
| 2c6aa72f3c | |||
| bd0afef790 | |||
| fc11cc0a1a | |||
| fb78303e8f | |||
| 2ea04440db | |||
| 96e1636be3 | |||
| c546451a73 | |||
| 61778053b4 | |||
| f5580d311d | |||
| 99d292bce0 | |||
| b2801641bc | |||
| bfaa1046b2 | |||
| 95c30400cc | |||
| e77480ee1d | |||
| 905800e535 | |||
| fadeaef4c6 | |||
| 437efda649 | |||
| dd75d5f54b | |||
| 392a2e582e | |||
| a1da183721 | |||
| feea2df0b1 | |||
| b47acd8c76 | |||
| 6fd87d9ced | |||
| acbb065808 | |||
| 2fb097061d | |||
| 8962d17e03 | |||
| 8326e1490c | |||
| 091e4d3e4c | |||
| 6ee77edcbb | |||
| 763e2288bf | |||
| 9cdb177ca7 | |||
| 6070508058 | |||
| ec13a5d84d | |||
| 057de82b01 | 
@ -1,5 +1,5 @@
 | 
				
			|||||||
[bumpversion]
 | 
					[bumpversion]
 | 
				
			||||||
current_version = 2024.8.3
 | 
					current_version = 2024.8.2
 | 
				
			||||||
tag = True
 | 
					tag = True
 | 
				
			||||||
commit = True
 | 
					commit = True
 | 
				
			||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
 | 
					parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@ -44,11 +44,9 @@ updates:
 | 
				
			|||||||
          - "babel-*"
 | 
					          - "babel-*"
 | 
				
			||||||
      eslint:
 | 
					      eslint:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@eslint/*"
 | 
					 | 
				
			||||||
          - "@typescript-eslint/*"
 | 
					          - "@typescript-eslint/*"
 | 
				
			||||||
          - "eslint-*"
 | 
					 | 
				
			||||||
          - "eslint"
 | 
					          - "eslint"
 | 
				
			||||||
          - "typescript-eslint"
 | 
					          - "eslint-*"
 | 
				
			||||||
      storybook:
 | 
					      storybook:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@storybook/*"
 | 
					          - "@storybook/*"
 | 
				
			||||||
@ -56,12 +54,10 @@ updates:
 | 
				
			|||||||
      esbuild:
 | 
					      esbuild:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@esbuild/*"
 | 
					          - "@esbuild/*"
 | 
				
			||||||
          - "esbuild*"
 | 
					 | 
				
			||||||
      rollup:
 | 
					      rollup:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@rollup/*"
 | 
					          - "@rollup/*"
 | 
				
			||||||
          - "rollup-*"
 | 
					          - "rollup-*"
 | 
				
			||||||
          - "rollup*"
 | 
					 | 
				
			||||||
      swc:
 | 
					      swc:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@swc/*"
 | 
					          - "@swc/*"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/api-ts-publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/api-ts-publish.yml
									
									
									
									
										vendored
									
									
								
							@ -40,7 +40,7 @@ jobs:
 | 
				
			|||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
 | 
					          export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
 | 
				
			||||||
          npm i @goauthentik/api@$VERSION
 | 
					          npm i @goauthentik/api@$VERSION
 | 
				
			||||||
      - uses: peter-evans/create-pull-request@v7
 | 
					      - uses: peter-evans/create-pull-request@v6
 | 
				
			||||||
        id: cpr
 | 
					        id: cpr
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          token: ${{ steps.generate_token.outputs.token }}
 | 
					          token: ${{ steps.generate_token.outputs.token }}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							@ -120,12 +120,6 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          flags: unit
 | 
					          flags: unit
 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					          token: ${{ secrets.CODECOV_TOKEN }}
 | 
				
			||||||
      - if: ${{ !cancelled() }}
 | 
					 | 
				
			||||||
        uses: codecov/test-results-action@v1
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          flags: unit
 | 
					 | 
				
			||||||
          file: unittest.xml
 | 
					 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					 | 
				
			||||||
  test-integration:
 | 
					  test-integration:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    timeout-minutes: 30
 | 
					    timeout-minutes: 30
 | 
				
			||||||
@ -144,12 +138,6 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          flags: integration
 | 
					          flags: integration
 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					          token: ${{ secrets.CODECOV_TOKEN }}
 | 
				
			||||||
      - if: ${{ !cancelled() }}
 | 
					 | 
				
			||||||
        uses: codecov/test-results-action@v1
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          flags: integration
 | 
					 | 
				
			||||||
          file: unittest.xml
 | 
					 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					 | 
				
			||||||
  test-e2e:
 | 
					  test-e2e:
 | 
				
			||||||
    name: test-e2e (${{ matrix.job.name }})
 | 
					    name: test-e2e (${{ matrix.job.name }})
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
@ -202,12 +190,6 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          flags: e2e
 | 
					          flags: e2e
 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					          token: ${{ secrets.CODECOV_TOKEN }}
 | 
				
			||||||
      - if: ${{ !cancelled() }}
 | 
					 | 
				
			||||||
        uses: codecov/test-results-action@v1
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          flags: e2e
 | 
					 | 
				
			||||||
          file: unittest.xml
 | 
					 | 
				
			||||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
					 | 
				
			||||||
  ci-core-mark:
 | 
					  ci-core-mark:
 | 
				
			||||||
    needs:
 | 
					    needs:
 | 
				
			||||||
      - lint
 | 
					      - lint
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							@ -45,6 +45,7 @@ jobs:
 | 
				
			|||||||
      - working-directory: ${{ matrix.project }}/
 | 
					      - working-directory: ${{ matrix.project }}/
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          npm ci
 | 
					          npm ci
 | 
				
			||||||
 | 
					          ${{ matrix.extra_setup }}
 | 
				
			||||||
      - name: Generate API
 | 
					      - name: Generate API
 | 
				
			||||||
        run: make gen-client-ts
 | 
					        run: make gen-client-ts
 | 
				
			||||||
      - name: Lint
 | 
					      - name: Lint
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ jobs:
 | 
				
			|||||||
      - name: Setup authentik env
 | 
					      - name: Setup authentik env
 | 
				
			||||||
        uses: ./.github/actions/setup
 | 
					        uses: ./.github/actions/setup
 | 
				
			||||||
      - run: poetry run ak update_webauthn_mds
 | 
					      - run: poetry run ak update_webauthn_mds
 | 
				
			||||||
      - uses: peter-evans/create-pull-request@v7
 | 
					      - uses: peter-evans/create-pull-request@v6
 | 
				
			||||||
        id: cpr
 | 
					        id: cpr
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          token: ${{ steps.generate_token.outputs.token }}
 | 
					          token: ${{ steps.generate_token.outputs.token }}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/image-compress.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/image-compress.yml
									
									
									
									
										vendored
									
									
								
							@ -42,7 +42,7 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          githubToken: ${{ steps.generate_token.outputs.token }}
 | 
					          githubToken: ${{ steps.generate_token.outputs.token }}
 | 
				
			||||||
          compressOnly: ${{ github.event_name != 'pull_request' }}
 | 
					          compressOnly: ${{ github.event_name != 'pull_request' }}
 | 
				
			||||||
      - uses: peter-evans/create-pull-request@v7
 | 
					      - uses: peter-evans/create-pull-request@v6
 | 
				
			||||||
        if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
 | 
					        if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
 | 
				
			||||||
        id: cpr
 | 
					        id: cpr
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ jobs:
 | 
				
			|||||||
          poetry run ak compilemessages
 | 
					          poetry run ak compilemessages
 | 
				
			||||||
          make web-check-compile
 | 
					          make web-check-compile
 | 
				
			||||||
      - name: Create Pull Request
 | 
					      - name: Create Pull Request
 | 
				
			||||||
        uses: peter-evans/create-pull-request@v7
 | 
					        uses: peter-evans/create-pull-request@v6
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          token: ${{ steps.generate_token.outputs.token }}
 | 
					          token: ${{ steps.generate_token.outputs.token }}
 | 
				
			||||||
          branch: extract-compile-backend-translation
 | 
					          branch: extract-compile-backend-translation
 | 
				
			||||||
 | 
				
			|||||||
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
 | 
				
			|||||||
    /bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
 | 
					    /bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Stage 5: Python dependencies
 | 
					# Stage 5: Python dependencies
 | 
				
			||||||
FROM ghcr.io/goauthentik/fips-python:3.12.6-slim-bookworm-fips-full AS python-deps
 | 
					FROM ghcr.io/goauthentik/fips-python:3.12.5-slim-bookworm-fips-full AS python-deps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ARG TARGETARCH
 | 
					ARG TARGETARCH
 | 
				
			||||||
ARG TARGETVARIANT
 | 
					ARG TARGETVARIANT
 | 
				
			||||||
@ -124,7 +124,7 @@ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
 | 
				
			|||||||
    pip install --force-reinstall /wheels/*"
 | 
					    pip install --force-reinstall /wheels/*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Stage 6: Run
 | 
					# Stage 6: Run
 | 
				
			||||||
FROM ghcr.io/goauthentik/fips-python:3.12.6-slim-bookworm-fips-full AS final-image
 | 
					FROM ghcr.io/goauthentik/fips-python:3.12.5-slim-bookworm-fips-full AS final-image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ARG VERSION
 | 
					ARG VERSION
 | 
				
			||||||
ARG GIT_BUILD_HASH
 | 
					ARG GIT_BUILD_HASH
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,8 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
| Version  | Supported |
 | 
					| Version  | Supported |
 | 
				
			||||||
| -------- | --------- |
 | 
					| -------- | --------- |
 | 
				
			||||||
 | 
					| 2024.4.x | ✅        |
 | 
				
			||||||
| 2024.6.x | ✅        |
 | 
					| 2024.6.x | ✅        |
 | 
				
			||||||
| 2024.8.x | ✅        |
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Reporting a Vulnerability
 | 
					## Reporting a Vulnerability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from os import environ
 | 
					from os import environ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__version__ = "2024.8.3"
 | 
					__version__ = "2024.8.2"
 | 
				
			||||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
 | 
					ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
"""authentik admin tasks"""
 | 
					"""authentik admin tasks"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
 | 
					from django.core.validators import URLValidator
 | 
				
			||||||
from django.db import DatabaseError, InternalError, ProgrammingError
 | 
					from django.db import DatabaseError, InternalError, ProgrammingError
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					 | 
				
			||||||
from packaging.version import parse
 | 
					from packaging.version import parse
 | 
				
			||||||
from requests import RequestException
 | 
					from requests import RequestException
 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
@ -19,6 +21,8 @@ LOGGER = get_logger()
 | 
				
			|||||||
VERSION_NULL = "0.0.0"
 | 
					VERSION_NULL = "0.0.0"
 | 
				
			||||||
VERSION_CACHE_KEY = "authentik_latest_version"
 | 
					VERSION_CACHE_KEY = "authentik_latest_version"
 | 
				
			||||||
VERSION_CACHE_TIMEOUT = 8 * 60 * 60  # 8 hours
 | 
					VERSION_CACHE_TIMEOUT = 8 * 60 * 60  # 8 hours
 | 
				
			||||||
 | 
					# Chop of the first ^ because we want to search the entire string
 | 
				
			||||||
 | 
					URL_FINDER = URLValidator.regex.pattern[1:]
 | 
				
			||||||
LOCAL_VERSION = parse(__version__)
 | 
					LOCAL_VERSION = parse(__version__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,16 +78,10 @@ def update_latest_version(self: SystemTask):
 | 
				
			|||||||
                context__new_version=upstream_version,
 | 
					                context__new_version=upstream_version,
 | 
				
			||||||
            ).exists():
 | 
					            ).exists():
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
            Event.new(
 | 
					            event_dict = {"new_version": upstream_version}
 | 
				
			||||||
                EventAction.UPDATE_AVAILABLE,
 | 
					            if match := re.search(URL_FINDER, data.get("stable", {}).get("changelog", "")):
 | 
				
			||||||
                message=_(
 | 
					                event_dict["message"] = f"Changelog: {match.group()}"
 | 
				
			||||||
                    "New version {version} available!".format(
 | 
					            Event.new(EventAction.UPDATE_AVAILABLE, **event_dict).save()
 | 
				
			||||||
                        version=upstream_version,
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                new_version=upstream_version,
 | 
					 | 
				
			||||||
                changelog=data.get("stable", {}).get("changelog_url"),
 | 
					 | 
				
			||||||
            ).save()
 | 
					 | 
				
			||||||
    except (RequestException, IndexError) as exc:
 | 
					    except (RequestException, IndexError) as exc:
 | 
				
			||||||
        cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
 | 
					        cache.set(VERSION_CACHE_KEY, VERSION_NULL, VERSION_CACHE_TIMEOUT)
 | 
				
			||||||
        self.set_error(exc)
 | 
					        self.set_error(exc)
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,6 @@ RESPONSE_VALID = {
 | 
				
			|||||||
    "stable": {
 | 
					    "stable": {
 | 
				
			||||||
        "version": "99999999.9999999",
 | 
					        "version": "99999999.9999999",
 | 
				
			||||||
        "changelog": "See https://goauthentik.io/test",
 | 
					        "changelog": "See https://goauthentik.io/test",
 | 
				
			||||||
        "changelog_url": "https://goauthentik.io/test",
 | 
					 | 
				
			||||||
        "reason": "bugfix",
 | 
					        "reason": "bugfix",
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -36,7 +35,7 @@ class TestAdminTasks(TestCase):
 | 
				
			|||||||
                Event.objects.filter(
 | 
					                Event.objects.filter(
 | 
				
			||||||
                    action=EventAction.UPDATE_AVAILABLE,
 | 
					                    action=EventAction.UPDATE_AVAILABLE,
 | 
				
			||||||
                    context__new_version="99999999.9999999",
 | 
					                    context__new_version="99999999.9999999",
 | 
				
			||||||
                    context__message="New version 99999999.9999999 available!",
 | 
					                    context__message="Changelog: https://goauthentik.io/test",
 | 
				
			||||||
                ).exists()
 | 
					                ).exists()
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            # test that a consecutive check doesn't create a duplicate event
 | 
					            # test that a consecutive check doesn't create a duplicate event
 | 
				
			||||||
@ -46,7 +45,7 @@ class TestAdminTasks(TestCase):
 | 
				
			|||||||
                    Event.objects.filter(
 | 
					                    Event.objects.filter(
 | 
				
			||||||
                        action=EventAction.UPDATE_AVAILABLE,
 | 
					                        action=EventAction.UPDATE_AVAILABLE,
 | 
				
			||||||
                        context__new_version="99999999.9999999",
 | 
					                        context__new_version="99999999.9999999",
 | 
				
			||||||
                        context__message="New version 99999999.9999999 available!",
 | 
					                        context__message="Changelog: https://goauthentik.io/test",
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                1,
 | 
					                1,
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,6 @@ from authentik.policies.models import PolicyBindingModel
 | 
				
			|||||||
from authentik.root.middleware import ClientIPMiddleware
 | 
					from authentik.root.middleware import ClientIPMiddleware
 | 
				
			||||||
from authentik.stages.email.utils import TemplateEmailMessage
 | 
					from authentik.stages.email.utils import TemplateEmailMessage
 | 
				
			||||||
from authentik.tenants.models import Tenant
 | 
					from authentik.tenants.models import Tenant
 | 
				
			||||||
from authentik.tenants.utils import get_current_tenant
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = get_logger()
 | 
					LOGGER = get_logger()
 | 
				
			||||||
DISCORD_FIELD_LIMIT = 25
 | 
					DISCORD_FIELD_LIMIT = 25
 | 
				
			||||||
@ -59,11 +58,7 @@ NOTIFICATION_SUMMARY_LENGTH = 75
 | 
				
			|||||||
def default_event_duration():
 | 
					def default_event_duration():
 | 
				
			||||||
    """Default duration an Event is saved.
 | 
					    """Default duration an Event is saved.
 | 
				
			||||||
    This is used as a fallback when no brand is available"""
 | 
					    This is used as a fallback when no brand is available"""
 | 
				
			||||||
    try:
 | 
					    return now() + timedelta(days=365)
 | 
				
			||||||
        tenant = get_current_tenant()
 | 
					 | 
				
			||||||
        return now() + timedelta_from_string(tenant.event_retention)
 | 
					 | 
				
			||||||
    except Tenant.DoesNotExist:
 | 
					 | 
				
			||||||
        return now() + timedelta(days=365)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def default_brand():
 | 
					def default_brand():
 | 
				
			||||||
@ -250,6 +245,12 @@ class Event(SerializerModel, ExpiringModel):
 | 
				
			|||||||
            if QS_QUERY in self.context["http_request"]["args"]:
 | 
					            if QS_QUERY in self.context["http_request"]["args"]:
 | 
				
			||||||
                wrapped = self.context["http_request"]["args"][QS_QUERY]
 | 
					                wrapped = self.context["http_request"]["args"][QS_QUERY]
 | 
				
			||||||
                self.context["http_request"]["args"] = cleanse_dict(QueryDict(wrapped))
 | 
					                self.context["http_request"]["args"] = cleanse_dict(QueryDict(wrapped))
 | 
				
			||||||
 | 
					        if hasattr(request, "tenant"):
 | 
				
			||||||
 | 
					            tenant: Tenant = request.tenant
 | 
				
			||||||
 | 
					            # Because self.created only gets set on save, we can't use it's value here
 | 
				
			||||||
 | 
					            # hence we set self.created to now and then use it
 | 
				
			||||||
 | 
					            self.created = now()
 | 
				
			||||||
 | 
					            self.expires = self.created + timedelta_from_string(tenant.event_retention)
 | 
				
			||||||
        if hasattr(request, "brand"):
 | 
					        if hasattr(request, "brand"):
 | 
				
			||||||
            brand: Brand = request.brand
 | 
					            brand: Brand = request.brand
 | 
				
			||||||
            self.brand = sanitize_dict(model_to_dict(brand))
 | 
					            self.brand = sanitize_dict(model_to_dict(brand))
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ from authentik.events.apps import SYSTEM_TASK_STATUS
 | 
				
			|||||||
from authentik.events.models import Event, EventAction, SystemTask
 | 
					from authentik.events.models import Event, EventAction, SystemTask
 | 
				
			||||||
from authentik.events.tasks import event_notification_handler, gdpr_cleanup
 | 
					from authentik.events.tasks import event_notification_handler, gdpr_cleanup
 | 
				
			||||||
from authentik.flows.models import Stage
 | 
					from authentik.flows.models import Stage
 | 
				
			||||||
from authentik.flows.planner import PLAN_CONTEXT_OUTPOST, PLAN_CONTEXT_SOURCE, FlowPlan
 | 
					from authentik.flows.planner import PLAN_CONTEXT_SOURCE, FlowPlan
 | 
				
			||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
 | 
					from authentik.flows.views.executor import SESSION_KEY_PLAN
 | 
				
			||||||
from authentik.root.monitoring import monitoring_set
 | 
					from authentik.root.monitoring import monitoring_set
 | 
				
			||||||
from authentik.stages.invitation.models import Invitation
 | 
					from authentik.stages.invitation.models import Invitation
 | 
				
			||||||
@ -38,9 +38,6 @@ def on_user_logged_in(sender, request: HttpRequest, user: User, **_):
 | 
				
			|||||||
            # Save the login method used
 | 
					            # Save the login method used
 | 
				
			||||||
            kwargs[PLAN_CONTEXT_METHOD] = flow_plan.context[PLAN_CONTEXT_METHOD]
 | 
					            kwargs[PLAN_CONTEXT_METHOD] = flow_plan.context[PLAN_CONTEXT_METHOD]
 | 
				
			||||||
            kwargs[PLAN_CONTEXT_METHOD_ARGS] = flow_plan.context.get(PLAN_CONTEXT_METHOD_ARGS, {})
 | 
					            kwargs[PLAN_CONTEXT_METHOD_ARGS] = flow_plan.context.get(PLAN_CONTEXT_METHOD_ARGS, {})
 | 
				
			||||||
        if PLAN_CONTEXT_OUTPOST in flow_plan.context:
 | 
					 | 
				
			||||||
            # Save outpost context
 | 
					 | 
				
			||||||
            kwargs[PLAN_CONTEXT_OUTPOST] = flow_plan.context[PLAN_CONTEXT_OUTPOST]
 | 
					 | 
				
			||||||
    event = Event.new(EventAction.LOGIN, **kwargs).from_http(request, user=user)
 | 
					    event = Event.new(EventAction.LOGIN, **kwargs).from_http(request, user=user)
 | 
				
			||||||
    request.session[SESSION_LOGIN_EVENT] = event
 | 
					    request.session[SESSION_LOGIN_EVENT] = event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@ from django.db.models import Model
 | 
				
			|||||||
from django.test import TestCase
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import default_token_key
 | 
					from authentik.core.models import default_token_key
 | 
				
			||||||
from authentik.events.models import default_event_duration
 | 
					 | 
				
			||||||
from authentik.lib.utils.reflection import get_apps
 | 
					from authentik.lib.utils.reflection import get_apps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,7 +20,7 @@ def model_tester_factory(test_model: type[Model]) -> Callable:
 | 
				
			|||||||
        allowed = 0
 | 
					        allowed = 0
 | 
				
			||||||
        # Token-like objects need to lookup the current tenant to get the default token length
 | 
					        # Token-like objects need to lookup the current tenant to get the default token length
 | 
				
			||||||
        for field in test_model._meta.fields:
 | 
					        for field in test_model._meta.fields:
 | 
				
			||||||
            if field.default in [default_token_key, default_event_duration]:
 | 
					            if field.default == default_token_key:
 | 
				
			||||||
                allowed += 1
 | 
					                allowed += 1
 | 
				
			||||||
        with self.assertNumQueries(allowed):
 | 
					        with self.assertNumQueries(allowed):
 | 
				
			||||||
            str(test_model())
 | 
					            str(test_model())
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,6 @@ from authentik.flows.models import (
 | 
				
			|||||||
    in_memory_stage,
 | 
					    in_memory_stage,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.lib.config import CONFIG
 | 
					from authentik.lib.config import CONFIG
 | 
				
			||||||
from authentik.outposts.models import Outpost
 | 
					 | 
				
			||||||
from authentik.policies.engine import PolicyEngine
 | 
					from authentik.policies.engine import PolicyEngine
 | 
				
			||||||
from authentik.root.middleware import ClientIPMiddleware
 | 
					from authentik.root.middleware import ClientIPMiddleware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -33,7 +32,6 @@ PLAN_CONTEXT_SSO = "is_sso"
 | 
				
			|||||||
PLAN_CONTEXT_REDIRECT = "redirect"
 | 
					PLAN_CONTEXT_REDIRECT = "redirect"
 | 
				
			||||||
PLAN_CONTEXT_APPLICATION = "application"
 | 
					PLAN_CONTEXT_APPLICATION = "application"
 | 
				
			||||||
PLAN_CONTEXT_SOURCE = "source"
 | 
					PLAN_CONTEXT_SOURCE = "source"
 | 
				
			||||||
PLAN_CONTEXT_OUTPOST = "outpost"
 | 
					 | 
				
			||||||
# Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan
 | 
					# Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan
 | 
				
			||||||
# was restored.
 | 
					# was restored.
 | 
				
			||||||
PLAN_CONTEXT_IS_RESTORED = "is_restored"
 | 
					PLAN_CONTEXT_IS_RESTORED = "is_restored"
 | 
				
			||||||
@ -145,23 +143,10 @@ class FlowPlanner:
 | 
				
			|||||||
            and not request.user.is_superuser
 | 
					            and not request.user.is_superuser
 | 
				
			||||||
        ):
 | 
					        ):
 | 
				
			||||||
            raise FlowNonApplicableException()
 | 
					            raise FlowNonApplicableException()
 | 
				
			||||||
        outpost_user = ClientIPMiddleware.get_outpost_user(request)
 | 
					 | 
				
			||||||
        if self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_OUTPOST:
 | 
					        if self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_OUTPOST:
 | 
				
			||||||
 | 
					            outpost_user = ClientIPMiddleware.get_outpost_user(request)
 | 
				
			||||||
            if not outpost_user:
 | 
					            if not outpost_user:
 | 
				
			||||||
                raise FlowNonApplicableException()
 | 
					                raise FlowNonApplicableException()
 | 
				
			||||||
        if outpost_user:
 | 
					 | 
				
			||||||
            outpost = Outpost.objects.filter(
 | 
					 | 
				
			||||||
                # TODO: Since Outpost and user are not directly connected, we have to look up a user
 | 
					 | 
				
			||||||
                # like this. This should ideally by in authentik/outposts/models.py
 | 
					 | 
				
			||||||
                pk=outpost_user.username.replace("ak-outpost-", "")
 | 
					 | 
				
			||||||
            ).first()
 | 
					 | 
				
			||||||
            if outpost:
 | 
					 | 
				
			||||||
                return {
 | 
					 | 
				
			||||||
                    PLAN_CONTEXT_OUTPOST: {
 | 
					 | 
				
			||||||
                        "instance": outpost,
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
        return {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def plan(self, request: HttpRequest, default_context: dict[str, Any] | None = None) -> FlowPlan:
 | 
					    def plan(self, request: HttpRequest, default_context: dict[str, Any] | None = None) -> FlowPlan:
 | 
				
			||||||
        """Check each of the flows' policies, check policies for each stage with PolicyBinding
 | 
					        """Check each of the flows' policies, check policies for each stage with PolicyBinding
 | 
				
			||||||
@ -174,12 +159,11 @@ class FlowPlanner:
 | 
				
			|||||||
            self._logger.debug(
 | 
					            self._logger.debug(
 | 
				
			||||||
                "f(plan): starting planning process",
 | 
					                "f(plan): starting planning process",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            context = default_context or {}
 | 
					 | 
				
			||||||
            # Bit of a workaround here, if there is a pending user set in the default context
 | 
					            # Bit of a workaround here, if there is a pending user set in the default context
 | 
				
			||||||
            # we use that user for our cache key
 | 
					            # we use that user for our cache key
 | 
				
			||||||
            # to make sure they don't get the generic response
 | 
					            # to make sure they don't get the generic response
 | 
				
			||||||
            if context and PLAN_CONTEXT_PENDING_USER in context:
 | 
					            if default_context and PLAN_CONTEXT_PENDING_USER in default_context:
 | 
				
			||||||
                user = context[PLAN_CONTEXT_PENDING_USER]
 | 
					                user = default_context[PLAN_CONTEXT_PENDING_USER]
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                user = request.user
 | 
					                user = request.user
 | 
				
			||||||
                # We only need to check the flow authentication if it's planned without a user
 | 
					                # We only need to check the flow authentication if it's planned without a user
 | 
				
			||||||
@ -187,13 +171,14 @@ class FlowPlanner:
 | 
				
			|||||||
                # or if a flow is restarted due to `invalid_response_action` being set to
 | 
					                # or if a flow is restarted due to `invalid_response_action` being set to
 | 
				
			||||||
                # `restart_with_context`, which can only happen if the user was already authorized
 | 
					                # `restart_with_context`, which can only happen if the user was already authorized
 | 
				
			||||||
                # to use the flow
 | 
					                # to use the flow
 | 
				
			||||||
                context.update(self._check_authentication(request))
 | 
					                self._check_authentication(request)
 | 
				
			||||||
            # First off, check the flow's direct policy bindings
 | 
					            # First off, check the flow's direct policy bindings
 | 
				
			||||||
            # to make sure the user even has access to the flow
 | 
					            # to make sure the user even has access to the flow
 | 
				
			||||||
            engine = PolicyEngine(self.flow, user, request)
 | 
					            engine = PolicyEngine(self.flow, user, request)
 | 
				
			||||||
            engine.use_cache = self.use_cache
 | 
					            engine.use_cache = self.use_cache
 | 
				
			||||||
            span.set_data("context", cleanse_dict(context))
 | 
					            if default_context:
 | 
				
			||||||
            engine.request.context.update(context)
 | 
					                span.set_data("default_context", cleanse_dict(default_context))
 | 
				
			||||||
 | 
					                engine.request.context.update(default_context)
 | 
				
			||||||
            engine.build()
 | 
					            engine.build()
 | 
				
			||||||
            result = engine.result
 | 
					            result = engine.result
 | 
				
			||||||
            if not result.passing:
 | 
					            if not result.passing:
 | 
				
			||||||
@ -210,12 +195,12 @@ class FlowPlanner:
 | 
				
			|||||||
                        key=cached_plan_key,
 | 
					                        key=cached_plan_key,
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    # Reset the context as this isn't factored into caching
 | 
					                    # Reset the context as this isn't factored into caching
 | 
				
			||||||
                    cached_plan.context = context
 | 
					                    cached_plan.context = default_context or {}
 | 
				
			||||||
                    return cached_plan
 | 
					                    return cached_plan
 | 
				
			||||||
            self._logger.debug(
 | 
					            self._logger.debug(
 | 
				
			||||||
                "f(plan): building plan",
 | 
					                "f(plan): building plan",
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            plan = self._build_plan(user, request, context)
 | 
					            plan = self._build_plan(user, request, default_context)
 | 
				
			||||||
            if self.use_cache:
 | 
					            if self.use_cache:
 | 
				
			||||||
                cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
 | 
					                cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
 | 
				
			||||||
            if not plan.bindings and not self.allow_empty_flows:
 | 
					            if not plan.bindings and not self.allow_empty_flows:
 | 
				
			||||||
 | 
				
			|||||||
@ -30,11 +30,6 @@ class TestHTTP(TestCase):
 | 
				
			|||||||
        request = self.factory.get("/", HTTP_X_FORWARDED_FOR="127.0.0.2")
 | 
					        request = self.factory.get("/", HTTP_X_FORWARDED_FOR="127.0.0.2")
 | 
				
			||||||
        self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.2")
 | 
					        self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_forward_for_invalid(self):
 | 
					 | 
				
			||||||
        """Test invalid forward for"""
 | 
					 | 
				
			||||||
        request = self.factory.get("/", HTTP_X_FORWARDED_FOR="foobar")
 | 
					 | 
				
			||||||
        self.assertEqual(ClientIPMiddleware.get_client_ip(request), ClientIPMiddleware.default_ip)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_fake_outpost(self):
 | 
					    def test_fake_outpost(self):
 | 
				
			||||||
        """Test faked IP which is overridden by an outpost"""
 | 
					        """Test faked IP which is overridden by an outpost"""
 | 
				
			||||||
        token = Token.objects.create(
 | 
					        token = Token.objects.create(
 | 
				
			||||||
@ -58,17 +53,6 @@ class TestHTTP(TestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
 | 
					        self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
 | 
				
			||||||
        # Invalid, not a real IP
 | 
					 | 
				
			||||||
        self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
 | 
					 | 
				
			||||||
        self.user.save()
 | 
					 | 
				
			||||||
        request = self.factory.get(
 | 
					 | 
				
			||||||
            "/",
 | 
					 | 
				
			||||||
            **{
 | 
					 | 
				
			||||||
                ClientIPMiddleware.outpost_remote_ip_header: "foobar",
 | 
					 | 
				
			||||||
                ClientIPMiddleware.outpost_token_header: token.key,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertEqual(ClientIPMiddleware.get_client_ip(request), "127.0.0.1")
 | 
					 | 
				
			||||||
        # Valid
 | 
					        # Valid
 | 
				
			||||||
        self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
 | 
					        self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
 | 
				
			||||||
        self.user.save()
 | 
					        self.user.save()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
# Generated by Django 5.0.9 on 2024-09-26 16:25
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					 | 
				
			||||||
from django.db import migrations, models
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					 | 
				
			||||||
        ("authentik_providers_oauth2", "0018_alter_accesstoken_expires_and_more"),
 | 
					 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    operations = [
 | 
					 | 
				
			||||||
        migrations.AddIndex(
 | 
					 | 
				
			||||||
            model_name="accesstoken",
 | 
					 | 
				
			||||||
            index=models.Index(fields=["token"], name="authentik_p_token_4bc870_idx"),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        migrations.AddIndex(
 | 
					 | 
				
			||||||
            model_name="refreshtoken",
 | 
					 | 
				
			||||||
            index=models.Index(fields=["token"], name="authentik_p_token_1a841f_idx"),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
@ -1,31 +0,0 @@
 | 
				
			|||||||
# Generated by Django 5.0.9 on 2024-09-27 14:50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					 | 
				
			||||||
from django.db import migrations, models
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Migration(migrations.Migration):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies = [
 | 
					 | 
				
			||||||
        ("authentik_providers_oauth2", "0019_accesstoken_authentik_p_token_4bc870_idx_and_more"),
 | 
					 | 
				
			||||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    operations = [
 | 
					 | 
				
			||||||
        migrations.RemoveIndex(
 | 
					 | 
				
			||||||
            model_name="accesstoken",
 | 
					 | 
				
			||||||
            name="authentik_p_token_4bc870_idx",
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        migrations.RemoveIndex(
 | 
					 | 
				
			||||||
            model_name="refreshtoken",
 | 
					 | 
				
			||||||
            name="authentik_p_token_1a841f_idx",
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        migrations.AddIndex(
 | 
					 | 
				
			||||||
            model_name="accesstoken",
 | 
					 | 
				
			||||||
            index=models.Index(fields=["token", "provider"], name="authentik_p_token_f99422_idx"),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        migrations.AddIndex(
 | 
					 | 
				
			||||||
            model_name="refreshtoken",
 | 
					 | 
				
			||||||
            index=models.Index(fields=["token", "provider"], name="authentik_p_token_a1d921_idx"),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
@ -376,9 +376,6 @@ class AccessToken(SerializerModel, ExpiringModel, BaseGrantModel):
 | 
				
			|||||||
    _id_token = models.TextField()
 | 
					    _id_token = models.TextField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        indexes = [
 | 
					 | 
				
			||||||
            models.Index(fields=["token", "provider"]),
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        verbose_name = _("OAuth2 Access Token")
 | 
					        verbose_name = _("OAuth2 Access Token")
 | 
				
			||||||
        verbose_name_plural = _("OAuth2 Access Tokens")
 | 
					        verbose_name_plural = _("OAuth2 Access Tokens")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -422,9 +419,6 @@ class RefreshToken(SerializerModel, ExpiringModel, BaseGrantModel):
 | 
				
			|||||||
    _id_token = models.TextField(verbose_name=_("ID Token"))
 | 
					    _id_token = models.TextField(verbose_name=_("ID Token"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        indexes = [
 | 
					 | 
				
			||||||
            models.Index(fields=["token", "provider"]),
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
        verbose_name = _("OAuth2 Refresh Token")
 | 
					        verbose_name = _("OAuth2 Refresh Token")
 | 
				
			||||||
        verbose_name_plural = _("OAuth2 Refresh Tokens")
 | 
					        verbose_name_plural = _("OAuth2 Refresh Tokens")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@ class TesOAuth2Introspection(OAuthTestCase):
 | 
				
			|||||||
        self.app = Application.objects.create(
 | 
					        self.app = Application.objects.create(
 | 
				
			||||||
            name=generate_id(), slug=generate_id(), provider=self.provider
 | 
					            name=generate_id(), slug=generate_id(), provider=self.provider
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        self.app.save()
 | 
				
			||||||
        self.user = create_test_admin_user()
 | 
					        self.user = create_test_admin_user()
 | 
				
			||||||
        self.auth = b64encode(
 | 
					        self.auth = b64encode(
 | 
				
			||||||
            f"{self.provider.client_id}:{self.provider.client_secret}".encode()
 | 
					            f"{self.provider.client_id}:{self.provider.client_secret}".encode()
 | 
				
			||||||
@ -113,41 +114,6 @@ class TesOAuth2Introspection(OAuthTestCase):
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_introspect_invalid_provider(self):
 | 
					 | 
				
			||||||
        """Test introspection (mismatched provider and token)"""
 | 
					 | 
				
			||||||
        provider: OAuth2Provider = OAuth2Provider.objects.create(
 | 
					 | 
				
			||||||
            name=generate_id(),
 | 
					 | 
				
			||||||
            authorization_flow=create_test_flow(),
 | 
					 | 
				
			||||||
            redirect_uris="",
 | 
					 | 
				
			||||||
            signing_key=create_test_cert(),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        auth = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        token: AccessToken = AccessToken.objects.create(
 | 
					 | 
				
			||||||
            provider=self.provider,
 | 
					 | 
				
			||||||
            user=self.user,
 | 
					 | 
				
			||||||
            token=generate_id(),
 | 
					 | 
				
			||||||
            auth_time=timezone.now(),
 | 
					 | 
				
			||||||
            _scope="openid user profile",
 | 
					 | 
				
			||||||
            _id_token=json.dumps(
 | 
					 | 
				
			||||||
                asdict(
 | 
					 | 
				
			||||||
                    IDToken("foo", "bar"),
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        res = self.client.post(
 | 
					 | 
				
			||||||
            reverse("authentik_providers_oauth2:token-introspection"),
 | 
					 | 
				
			||||||
            HTTP_AUTHORIZATION=f"Basic {auth}",
 | 
					 | 
				
			||||||
            data={"token": token.token},
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertEqual(res.status_code, 200)
 | 
					 | 
				
			||||||
        self.assertJSONEqual(
 | 
					 | 
				
			||||||
            res.content.decode(),
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "active": False,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_introspect_invalid_auth(self):
 | 
					    def test_introspect_invalid_auth(self):
 | 
				
			||||||
        """Test introspect (invalid auth)"""
 | 
					        """Test introspect (invalid auth)"""
 | 
				
			||||||
        res = self.client.post(
 | 
					        res = self.client.post(
 | 
				
			||||||
 | 
				
			|||||||
@ -46,10 +46,10 @@ class TokenIntrospectionParams:
 | 
				
			|||||||
        if not provider:
 | 
					        if not provider:
 | 
				
			||||||
            raise TokenIntrospectionError
 | 
					            raise TokenIntrospectionError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        access_token = AccessToken.objects.filter(token=raw_token, provider=provider).first()
 | 
					        access_token = AccessToken.objects.filter(token=raw_token).first()
 | 
				
			||||||
        if access_token:
 | 
					        if access_token:
 | 
				
			||||||
            return TokenIntrospectionParams(access_token, provider)
 | 
					            return TokenIntrospectionParams(access_token, provider)
 | 
				
			||||||
        refresh_token = RefreshToken.objects.filter(token=raw_token, provider=provider).first()
 | 
					        refresh_token = RefreshToken.objects.filter(token=raw_token).first()
 | 
				
			||||||
        if refresh_token:
 | 
					        if refresh_token:
 | 
				
			||||||
            return TokenIntrospectionParams(refresh_token, provider)
 | 
					            return TokenIntrospectionParams(refresh_token, provider)
 | 
				
			||||||
        LOGGER.debug("Token does not exist", token=raw_token)
 | 
					        LOGGER.debug("Token does not exist", token=raw_token)
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@ class ProxyDockerController(DockerController):
 | 
				
			|||||||
        labels = super()._get_labels()
 | 
					        labels = super()._get_labels()
 | 
				
			||||||
        labels["traefik.enable"] = "true"
 | 
					        labels["traefik.enable"] = "true"
 | 
				
			||||||
        labels[f"traefik.http.routers.{traefik_name}-router.rule"] = (
 | 
					        labels[f"traefik.http.routers.{traefik_name}-router.rule"] = (
 | 
				
			||||||
            f"({' || '.join([f'Host({host})' for host in hosts])})"
 | 
					            f"({' || '.join([f'Host(`{host}`)' for host in hosts])})"
 | 
				
			||||||
            f" && PathPrefix(`/outpost.goauthentik.io`)"
 | 
					            f" && PathPrefix(`/outpost.goauthentik.io`)"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        labels[f"traefik.http.routers.{traefik_name}-router.tls"] = "true"
 | 
					        labels[f"traefik.http.routers.{traefik_name}-router.tls"] = "true"
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from collections.abc import Callable
 | 
					from collections.abc import Callable
 | 
				
			||||||
from hashlib import sha512
 | 
					from hashlib import sha512
 | 
				
			||||||
from ipaddress import ip_address
 | 
					 | 
				
			||||||
from time import perf_counter, time
 | 
					from time import perf_counter, time
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -175,7 +174,6 @@ class ClientIPMiddleware:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
 | 
					    def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]):
 | 
				
			||||||
        self.get_response = get_response
 | 
					        self.get_response = get_response
 | 
				
			||||||
        self.logger = get_logger().bind()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_client_ip_from_meta(self, meta: dict[str, Any]) -> str:
 | 
					    def _get_client_ip_from_meta(self, meta: dict[str, Any]) -> str:
 | 
				
			||||||
        """Attempt to get the client's IP by checking common HTTP Headers.
 | 
					        """Attempt to get the client's IP by checking common HTTP Headers.
 | 
				
			||||||
@ -187,16 +185,11 @@ class ClientIPMiddleware:
 | 
				
			|||||||
            "HTTP_X_FORWARDED_FOR",
 | 
					            "HTTP_X_FORWARDED_FOR",
 | 
				
			||||||
            "REMOTE_ADDR",
 | 
					            "REMOTE_ADDR",
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        try:
 | 
					        for _header in headers:
 | 
				
			||||||
            for _header in headers:
 | 
					            if _header in meta:
 | 
				
			||||||
                if _header in meta:
 | 
					                ips: list[str] = meta.get(_header).split(",")
 | 
				
			||||||
                    ips: list[str] = meta.get(_header).split(",")
 | 
					                return ips[0].strip()
 | 
				
			||||||
                    # Ensure the IP parses as a valid IP
 | 
					        return self.default_ip
 | 
				
			||||||
                    return str(ip_address(ips[0].strip()))
 | 
					 | 
				
			||||||
            return self.default_ip
 | 
					 | 
				
			||||||
        except ValueError as exc:
 | 
					 | 
				
			||||||
            self.logger.debug("Invalid remote IP", exc=exc)
 | 
					 | 
				
			||||||
            return self.default_ip
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # FIXME: this should probably not be in `root` but rather in a middleware in `outposts`
 | 
					    # FIXME: this should probably not be in `root` but rather in a middleware in `outposts`
 | 
				
			||||||
    # but for now it's fine
 | 
					    # but for now it's fine
 | 
				
			||||||
@ -228,16 +221,12 @@ class ClientIPMiddleware:
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
        # Update sentry scope to include correct IP
 | 
					        # Update sentry scope to include correct IP
 | 
				
			||||||
        sentry_user = Scope.get_isolation_scope()._user or {}
 | 
					        user = Scope.get_isolation_scope()._user or {}
 | 
				
			||||||
        sentry_user["ip_address"] = delegated_ip
 | 
					        user["ip_address"] = delegated_ip
 | 
				
			||||||
        Scope.get_isolation_scope().set_user(sentry_user)
 | 
					        Scope.get_isolation_scope().set_user(user)
 | 
				
			||||||
        # Set the outpost service account on the request
 | 
					        # Set the outpost service account on the request
 | 
				
			||||||
        setattr(request, self.request_attr_outpost_user, user)
 | 
					        setattr(request, self.request_attr_outpost_user, user)
 | 
				
			||||||
        try:
 | 
					        return delegated_ip
 | 
				
			||||||
            return str(ip_address(delegated_ip))
 | 
					 | 
				
			||||||
        except ValueError as exc:
 | 
					 | 
				
			||||||
            self.logger.debug("Invalid remote IP from Outpost", exc=exc)
 | 
					 | 
				
			||||||
            return None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_client_ip(self, request: HttpRequest | None) -> str:
 | 
					    def _get_client_ip(self, request: HttpRequest | None) -> str:
 | 
				
			||||||
        """Attempt to get the client's IP by checking common HTTP Headers.
 | 
					        """Attempt to get the client's IP by checking common HTTP Headers.
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.core.cache import cache
 | 
					from django.core.cache import cache
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					 | 
				
			||||||
from drf_spectacular.utils import extend_schema, inline_serializer
 | 
					from drf_spectacular.utils import extend_schema, inline_serializer
 | 
				
			||||||
from guardian.shortcuts import get_objects_for_user
 | 
					from guardian.shortcuts import get_objects_for_user
 | 
				
			||||||
from rest_framework.decorators import action
 | 
					from rest_framework.decorators import action
 | 
				
			||||||
@ -40,8 +39,9 @@ class LDAPSourceSerializer(SourceSerializer):
 | 
				
			|||||||
        """Get cached source connectivity"""
 | 
					        """Get cached source connectivity"""
 | 
				
			||||||
        return cache.get(CACHE_KEY_STATUS + source.slug, None)
 | 
					        return cache.get(CACHE_KEY_STATUS + source.slug, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def validate_sync_users_password(self, sync_users_password: bool) -> bool:
 | 
					    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
 | 
				
			||||||
        """Check that only a single source has password_sync on"""
 | 
					        """Check that only a single source has password_sync on"""
 | 
				
			||||||
 | 
					        sync_users_password = attrs.get("sync_users_password", True)
 | 
				
			||||||
        if sync_users_password:
 | 
					        if sync_users_password:
 | 
				
			||||||
            sources = LDAPSource.objects.filter(sync_users_password=True)
 | 
					            sources = LDAPSource.objects.filter(sync_users_password=True)
 | 
				
			||||||
            if self.instance:
 | 
					            if self.instance:
 | 
				
			||||||
@ -49,31 +49,11 @@ class LDAPSourceSerializer(SourceSerializer):
 | 
				
			|||||||
            if sources.exists():
 | 
					            if sources.exists():
 | 
				
			||||||
                raise ValidationError(
 | 
					                raise ValidationError(
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        "sync_users_password": _(
 | 
					                        "sync_users_password": (
 | 
				
			||||||
                            "Only a single LDAP Source with password synchronization is allowed"
 | 
					                            "Only a single LDAP Source with password synchronization is allowed"
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        return sync_users_password
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
 | 
					 | 
				
			||||||
        """Validate property mappings with sync_ flags"""
 | 
					 | 
				
			||||||
        types = ["user", "group"]
 | 
					 | 
				
			||||||
        for type in types:
 | 
					 | 
				
			||||||
            toggle_value = attrs.get(f"sync_{type}s", False)
 | 
					 | 
				
			||||||
            mappings_field = f"{type}_property_mappings"
 | 
					 | 
				
			||||||
            mappings_value = attrs.get(mappings_field, [])
 | 
					 | 
				
			||||||
            if toggle_value and len(mappings_value) == 0:
 | 
					 | 
				
			||||||
                raise ValidationError(
 | 
					 | 
				
			||||||
                    {
 | 
					 | 
				
			||||||
                        mappings_field: _(
 | 
					 | 
				
			||||||
                            (
 | 
					 | 
				
			||||||
                                "When 'Sync {type}s' is enabled, '{type}s property "
 | 
					 | 
				
			||||||
                                "mappings' cannot be empty."
 | 
					 | 
				
			||||||
                            ).format(type=type)
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
        return super().validate(attrs)
 | 
					        return super().validate(attrs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
@ -186,12 +166,11 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet):
 | 
				
			|||||||
        for sync_class in SYNC_CLASSES:
 | 
					        for sync_class in SYNC_CLASSES:
 | 
				
			||||||
            class_name = sync_class.name()
 | 
					            class_name = sync_class.name()
 | 
				
			||||||
            all_objects.setdefault(class_name, [])
 | 
					            all_objects.setdefault(class_name, [])
 | 
				
			||||||
            for page in sync_class(source).get_objects(size_limit=10):
 | 
					            for obj in sync_class(source).get_objects(size_limit=10):
 | 
				
			||||||
                for obj in page:
 | 
					                obj: dict
 | 
				
			||||||
                    obj: dict
 | 
					                obj.pop("raw_attributes", None)
 | 
				
			||||||
                    obj.pop("raw_attributes", None)
 | 
					                obj.pop("raw_dn", None)
 | 
				
			||||||
                    obj.pop("raw_dn", None)
 | 
					                all_objects[class_name].append(obj)
 | 
				
			||||||
                    all_objects[class_name].append(obj)
 | 
					 | 
				
			||||||
        return Response(data=all_objects)
 | 
					        return Response(data=all_objects)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -26,16 +26,17 @@ def sync_ldap_source_on_save(sender, instance: LDAPSource, **_):
 | 
				
			|||||||
    """Ensure that source is synced on save (if enabled)"""
 | 
					    """Ensure that source is synced on save (if enabled)"""
 | 
				
			||||||
    if not instance.enabled:
 | 
					    if not instance.enabled:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    ldap_connectivity_check.delay(instance.pk)
 | 
					 | 
				
			||||||
    # Don't sync sources when they don't have any property mappings. This will only happen if:
 | 
					    # Don't sync sources when they don't have any property mappings. This will only happen if:
 | 
				
			||||||
    # - the user forgets to set them or
 | 
					    # - the user forgets to set them or
 | 
				
			||||||
    # - the source is newly created, this is the first save event
 | 
					    # - the source is newly created, this is the first save event
 | 
				
			||||||
    #   and the mappings are created with an m2m event
 | 
					    #   and the mappings are created with an m2m event
 | 
				
			||||||
    if instance.sync_users and not instance.user_property_mappings.exists():
 | 
					    if (
 | 
				
			||||||
        return
 | 
					        not instance.user_property_mappings.exists()
 | 
				
			||||||
    if instance.sync_groups and not instance.group_property_mappings.exists():
 | 
					        or not instance.group_property_mappings.exists()
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    ldap_sync_single.delay(instance.pk)
 | 
					    ldap_sync_single.delay(instance.pk)
 | 
				
			||||||
 | 
					    ldap_connectivity_check.delay(instance.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(password_validate)
 | 
					@receiver(password_validate)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								authentik/sources/ldap/sync/vendor/ms_ad.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								authentik/sources/ldap/sync/vendor/ms_ad.py
									
									
									
									
										vendored
									
									
								
							@ -78,9 +78,7 @@ class MicrosoftActiveDirectory(BaseLDAPSynchronizer):
 | 
				
			|||||||
        #   /useraccountcontrol-manipulate-account-properties
 | 
					        #   /useraccountcontrol-manipulate-account-properties
 | 
				
			||||||
        uac_bit = attributes.get("userAccountControl", 512)
 | 
					        uac_bit = attributes.get("userAccountControl", 512)
 | 
				
			||||||
        uac = UserAccountControl(uac_bit)
 | 
					        uac = UserAccountControl(uac_bit)
 | 
				
			||||||
        is_active = (
 | 
					        is_active = UserAccountControl.ACCOUNTDISABLE not in uac
 | 
				
			||||||
            UserAccountControl.ACCOUNTDISABLE not in uac and UserAccountControl.LOCKOUT not in uac
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        if is_active != user.is_active:
 | 
					        if is_active != user.is_active:
 | 
				
			||||||
            user.is_active = is_active
 | 
					            user.is_active = is_active
 | 
				
			||||||
            user.save()
 | 
					            user.save()
 | 
				
			||||||
 | 
				
			|||||||
@ -50,35 +50,3 @@ class LDAPAPITests(APITestCase):
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.assertFalse(serializer.is_valid())
 | 
					        self.assertFalse(serializer.is_valid())
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_sync_users_mapping_empty(self):
 | 
					 | 
				
			||||||
        """Check that when sync_users is enabled, property mappings must be set"""
 | 
					 | 
				
			||||||
        serializer = LDAPSourceSerializer(
 | 
					 | 
				
			||||||
            data={
 | 
					 | 
				
			||||||
                "name": "foo",
 | 
					 | 
				
			||||||
                "slug": " foo",
 | 
					 | 
				
			||||||
                "server_uri": "ldaps://1.2.3.4",
 | 
					 | 
				
			||||||
                "bind_cn": "",
 | 
					 | 
				
			||||||
                "bind_password": LDAP_PASSWORD,
 | 
					 | 
				
			||||||
                "base_dn": "dc=foo",
 | 
					 | 
				
			||||||
                "sync_users": True,
 | 
					 | 
				
			||||||
                "user_property_mappings": [],
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(serializer.is_valid())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_sync_groups_mapping_empty(self):
 | 
					 | 
				
			||||||
        """Check that when sync_groups is enabled, property mappings must be set"""
 | 
					 | 
				
			||||||
        serializer = LDAPSourceSerializer(
 | 
					 | 
				
			||||||
            data={
 | 
					 | 
				
			||||||
                "name": "foo",
 | 
					 | 
				
			||||||
                "slug": " foo",
 | 
					 | 
				
			||||||
                "server_uri": "ldaps://1.2.3.4",
 | 
					 | 
				
			||||||
                "bind_cn": "",
 | 
					 | 
				
			||||||
                "bind_password": LDAP_PASSWORD,
 | 
					 | 
				
			||||||
                "base_dn": "dc=foo",
 | 
					 | 
				
			||||||
                "sync_groups": True,
 | 
					 | 
				
			||||||
                "group_property_mappings": [],
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.assertFalse(serializer.is_valid())
 | 
					 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -82,5 +82,3 @@ entries:
 | 
				
			|||||||
    order: 10
 | 
					    order: 10
 | 
				
			||||||
    target: !KeyOf default-authentication-flow-password-binding
 | 
					    target: !KeyOf default-authentication-flow-password-binding
 | 
				
			||||||
    policy: !KeyOf default-authentication-flow-password-optional
 | 
					    policy: !KeyOf default-authentication-flow-password-optional
 | 
				
			||||||
  attrs:
 | 
					 | 
				
			||||||
    failure_result: true
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
    "$schema": "http://json-schema.org/draft-07/schema",
 | 
					    "$schema": "http://json-schema.org/draft-07/schema",
 | 
				
			||||||
    "$id": "https://goauthentik.io/blueprints/schema.json",
 | 
					    "$id": "https://goauthentik.io/blueprints/schema.json",
 | 
				
			||||||
    "type": "object",
 | 
					    "type": "object",
 | 
				
			||||||
    "title": "authentik 2024.8.3 Blueprint schema",
 | 
					    "title": "authentik 2024.8.2 Blueprint schema",
 | 
				
			||||||
    "required": [
 | 
					    "required": [
 | 
				
			||||||
        "version",
 | 
					        "version",
 | 
				
			||||||
        "entries"
 | 
					        "entries"
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ services:
 | 
				
			|||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - redis:/data
 | 
					      - redis:/data
 | 
				
			||||||
  server:
 | 
					  server:
 | 
				
			||||||
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.3}
 | 
					    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.2}
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
    command: server
 | 
					    command: server
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
@ -52,7 +52,7 @@ services:
 | 
				
			|||||||
      - postgresql
 | 
					      - postgresql
 | 
				
			||||||
      - redis
 | 
					      - redis
 | 
				
			||||||
  worker:
 | 
					  worker:
 | 
				
			||||||
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.3}
 | 
					    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.2}
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
    command: worker
 | 
					    command: worker
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
									
									
									
									
								
							@ -7,7 +7,7 @@ toolchain go1.23.0
 | 
				
			|||||||
require (
 | 
					require (
 | 
				
			||||||
	beryju.io/ldap v0.1.0
 | 
						beryju.io/ldap v0.1.0
 | 
				
			||||||
	github.com/coreos/go-oidc/v3 v3.11.0
 | 
						github.com/coreos/go-oidc/v3 v3.11.0
 | 
				
			||||||
	github.com/getsentry/sentry-go v0.29.0
 | 
						github.com/getsentry/sentry-go v0.28.1
 | 
				
			||||||
	github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
 | 
						github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
 | 
				
			||||||
	github.com/go-ldap/ldap/v3 v3.4.8
 | 
						github.com/go-ldap/ldap/v3 v3.4.8
 | 
				
			||||||
	github.com/go-openapi/runtime v0.28.0
 | 
						github.com/go-openapi/runtime v0.28.0
 | 
				
			||||||
@ -18,20 +18,20 @@ require (
 | 
				
			|||||||
	github.com/gorilla/securecookie v1.1.2
 | 
						github.com/gorilla/securecookie v1.1.2
 | 
				
			||||||
	github.com/gorilla/sessions v1.4.0
 | 
						github.com/gorilla/sessions v1.4.0
 | 
				
			||||||
	github.com/gorilla/websocket v1.5.3
 | 
						github.com/gorilla/websocket v1.5.3
 | 
				
			||||||
	github.com/jellydator/ttlcache/v3 v3.3.0
 | 
						github.com/jellydator/ttlcache/v3 v3.2.1
 | 
				
			||||||
	github.com/mitchellh/mapstructure v1.5.0
 | 
						github.com/mitchellh/mapstructure v1.5.0
 | 
				
			||||||
	github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
 | 
						github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
 | 
				
			||||||
	github.com/pires/go-proxyproto v0.7.0
 | 
						github.com/pires/go-proxyproto v0.7.0
 | 
				
			||||||
	github.com/prometheus/client_golang v1.20.4
 | 
						github.com/prometheus/client_golang v1.20.2
 | 
				
			||||||
	github.com/redis/go-redis/v9 v9.6.1
 | 
						github.com/redis/go-redis/v9 v9.6.1
 | 
				
			||||||
	github.com/sethvargo/go-envconfig v1.1.0
 | 
						github.com/sethvargo/go-envconfig v1.1.0
 | 
				
			||||||
	github.com/sirupsen/logrus v1.9.3
 | 
						github.com/sirupsen/logrus v1.9.3
 | 
				
			||||||
	github.com/spf13/cobra v1.8.1
 | 
						github.com/spf13/cobra v1.8.1
 | 
				
			||||||
	github.com/stretchr/testify v1.9.0
 | 
						github.com/stretchr/testify v1.9.0
 | 
				
			||||||
	github.com/wwt/guac v1.3.2
 | 
						github.com/wwt/guac v1.3.2
 | 
				
			||||||
	goauthentik.io/api/v3 v3.2024083.1
 | 
						goauthentik.io/api/v3 v3.2024064.1
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
 | 
						golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
 | 
				
			||||||
	golang.org/x/oauth2 v0.23.0
 | 
						golang.org/x/oauth2 v0.22.0
 | 
				
			||||||
	golang.org/x/sync v0.8.0
 | 
						golang.org/x/sync v0.8.0
 | 
				
			||||||
	gopkg.in/yaml.v2 v2.4.0
 | 
						gopkg.in/yaml.v2 v2.4.0
 | 
				
			||||||
	layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
 | 
						layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								go.sum
									
									
									
									
									
								
							@ -69,8 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
 | 
				
			|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
					github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 | 
				
			||||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 | 
					github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 | 
				
			||||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 | 
					github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA=
 | 
					github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY=
 | 
					github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
 | 
				
			||||||
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
 | 
					github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
 | 
				
			||||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 | 
					github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 | 
				
			||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 | 
					github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 | 
				
			||||||
@ -200,8 +200,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
 | 
				
			|||||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
 | 
					github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
 | 
				
			||||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
 | 
					github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
 | 
				
			||||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
 | 
					github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
 | 
				
			||||||
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
 | 
					github.com/jellydator/ttlcache/v3 v3.2.1 h1:eS8ljnYY7BllYGkXw/TfczWZrXUu/CH7SIkC6ugn9Js=
 | 
				
			||||||
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
 | 
					github.com/jellydator/ttlcache/v3 v3.2.1/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
 | 
				
			||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 | 
					github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 | 
				
			||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
					github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 | 
				
			||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
					github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
				
			||||||
@ -239,8 +239,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
 | 
					github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
 | 
				
			||||||
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 | 
					github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 | 
				
			||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
					github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
				
			||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 | 
					github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 | 
				
			||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 | 
					github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 | 
				
			||||||
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
 | 
				
			|||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
 | 
					go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
 | 
				
			||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 | 
					go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 | 
				
			||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 | 
					go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 | 
				
			||||||
goauthentik.io/api/v3 v3.2024083.1 h1:OPo2VejMkS5WjYw5zIjfuxR9XUbTKs4m+sACrPKcm9U=
 | 
					goauthentik.io/api/v3 v3.2024064.1 h1:vxquklgDGD+nGFhWRAsQ7ezQKg17MRq6bzEk25fbsb4=
 | 
				
			||||||
goauthentik.io/api/v3 v3.2024083.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
 | 
					goauthentik.io/api/v3 v3.2024064.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
					golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
@ -388,8 +388,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
 | 
				
			|||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
					golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
				
			||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
					golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 | 
				
			||||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
 | 
					golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
 | 
				
			||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 | 
					golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
 | 
				
			|||||||
@ -29,4 +29,4 @@ func UserAgent() string {
 | 
				
			|||||||
	return fmt.Sprintf("authentik@%s", FullVersion())
 | 
						return fmt.Sprintf("authentik@%s", FullVersion())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const VERSION = "2024.8.3"
 | 
					const VERSION = "2024.8.2"
 | 
				
			||||||
 | 
				
			|||||||
@ -65,11 +65,8 @@ type Server interface {
 | 
				
			|||||||
	CryptoStore() *ak.CryptoStore
 | 
						CryptoStore() *ak.CryptoStore
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server) (*Application, error) {
 | 
				
			||||||
	gob.Register(Claims{})
 | 
						gob.Register(Claims{})
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, oldApp *Application) (*Application, error) {
 | 
					 | 
				
			||||||
	muxLogger := log.WithField("logger", "authentik.outpost.proxyv2.application").WithField("name", p.Name)
 | 
						muxLogger := log.WithField("logger", "authentik.outpost.proxyv2.application").WithField("name", p.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	externalHost, err := url.Parse(p.ExternalHost)
 | 
						externalHost, err := url.Parse(p.ExternalHost)
 | 
				
			||||||
@ -140,15 +137,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
 | 
				
			|||||||
		isEmbedded:           isEmbedded,
 | 
							isEmbedded:           isEmbedded,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	go a.authHeaderCache.Start()
 | 
						go a.authHeaderCache.Start()
 | 
				
			||||||
	if oldApp != nil && oldApp.sessions != nil {
 | 
						a.sessions = a.getStore(p, externalHost)
 | 
				
			||||||
		a.sessions = oldApp.sessions
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		sess, err := a.getStore(p, externalHost)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		a.sessions = sess
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mux.Use(web.NewLoggingHandler(muxLogger, func(l *log.Entry, r *http.Request) *log.Entry {
 | 
						mux.Use(web.NewLoggingHandler(muxLogger, func(l *log.Entry, r *http.Request) *log.Entry {
 | 
				
			||||||
		c := a.getClaimsFromSession(r)
 | 
							c := a.getClaimsFromSession(r)
 | 
				
			||||||
		if c == nil {
 | 
							if c == nil {
 | 
				
			||||||
@ -246,8 +235,9 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
 | 
				
			|||||||
				// TODO: maybe create event for this?
 | 
									// TODO: maybe create event for this?
 | 
				
			||||||
				a.log.WithError(err).Warning("failed to compile SkipPathRegex")
 | 
									a.log.WithError(err).Warning("failed to compile SkipPathRegex")
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									a.UnauthenticatedRegex = append(a.UnauthenticatedRegex, re)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			a.UnauthenticatedRegex = append(a.UnauthenticatedRegex, re)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return a, nil
 | 
						return a, nil
 | 
				
			||||||
 | 
				
			|||||||
@ -26,7 +26,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const RedisKeyPrefix = "authentik_proxy_session_"
 | 
					const RedisKeyPrefix = "authentik_proxy_session_"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL) (sessions.Store, error) {
 | 
					func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL) sessions.Store {
 | 
				
			||||||
	maxAge := 0
 | 
						maxAge := 0
 | 
				
			||||||
	if p.AccessTokenValidity.IsSet() {
 | 
						if p.AccessTokenValidity.IsSet() {
 | 
				
			||||||
		t := p.AccessTokenValidity.Get()
 | 
							t := p.AccessTokenValidity.Get()
 | 
				
			||||||
@ -73,7 +73,7 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
 | 
				
			|||||||
		// New default RedisStore
 | 
							// New default RedisStore
 | 
				
			||||||
		rs, err := redisstore.NewRedisStore(context.Background(), client)
 | 
							rs, err := redisstore.NewRedisStore(context.Background(), client)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								a.log.WithError(err).Panic("failed to connect to redis")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rs.KeyPrefix(RedisKeyPrefix)
 | 
							rs.KeyPrefix(RedisKeyPrefix)
 | 
				
			||||||
@ -87,7 +87,7 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		a.log.Trace("using redis session backend")
 | 
							a.log.Trace("using redis session backend")
 | 
				
			||||||
		return rs, nil
 | 
							return rs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	dir := os.TempDir()
 | 
						dir := os.TempDir()
 | 
				
			||||||
	cs := sessions.NewFilesystemStore(dir)
 | 
						cs := sessions.NewFilesystemStore(dir)
 | 
				
			||||||
@ -106,7 +106,7 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
 | 
				
			|||||||
	cs.Options.MaxAge = maxAge
 | 
						cs.Options.MaxAge = maxAge
 | 
				
			||||||
	cs.Options.Path = "/"
 | 
						cs.Options.Path = "/"
 | 
				
			||||||
	a.log.WithField("dir", dir).Trace("using filesystem session backend")
 | 
						a.log.WithField("dir", dir).Trace("using filesystem session backend")
 | 
				
			||||||
	return cs, nil
 | 
						return cs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *Application) SessionName() string {
 | 
					func (a *Application) SessionName() string {
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,6 @@ func newTestApplication() *Application {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		http.DefaultClient,
 | 
							http.DefaultClient,
 | 
				
			||||||
		ts,
 | 
							ts,
 | 
				
			||||||
		nil,
 | 
					 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	ts.apps = append(ts.apps, a)
 | 
						ts.apps = append(ts.apps, a)
 | 
				
			||||||
	return a
 | 
						return a
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,6 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/getsentry/sentry-go"
 | 
						"github.com/getsentry/sentry-go"
 | 
				
			||||||
	"goauthentik.io/internal/constants"
 | 
						"goauthentik.io/internal/constants"
 | 
				
			||||||
@ -38,21 +37,16 @@ func (ps *ProxyServer) Refresh() error {
 | 
				
			|||||||
				),
 | 
									),
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		externalHost, err := url.Parse(provider.ExternalHost)
 | 
							a, err := application.NewApplication(provider, hc, ps)
 | 
				
			||||||
		if err != nil {
 | 
							existing, ok := ps.apps[a.Host]
 | 
				
			||||||
			ps.log.WithError(err).Warning("failed to parse URL, skipping provider")
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		existing, ok := ps.apps[externalHost.Host]
 | 
					 | 
				
			||||||
		a, err := application.NewApplication(provider, hc, ps, existing)
 | 
					 | 
				
			||||||
		if ok {
 | 
							if ok {
 | 
				
			||||||
			existing.Stop()
 | 
								existing.Stop()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ps.log.WithError(err).Warning("failed to setup application")
 | 
								ps.log.WithError(err).Warning("failed to setup application")
 | 
				
			||||||
			continue
 | 
							} else {
 | 
				
			||||||
 | 
								apps[a.Host] = a
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		apps[externalHost.Host] = a
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ps.apps = apps
 | 
						ps.apps = apps
 | 
				
			||||||
	ps.log.Debug("Swapped maps")
 | 
						ps.log.Debug("Swapped maps")
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2024-09-25 00:08+0000\n"
 | 
					"POT-Creation-Date: 2024-08-18 00:08+0000\n"
 | 
				
			||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
					"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
				
			||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
					"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
				
			||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
					"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
				
			||||||
@ -18,11 +18,6 @@ msgstr ""
 | 
				
			|||||||
"Content-Transfer-Encoding: 8bit\n"
 | 
					"Content-Transfer-Encoding: 8bit\n"
 | 
				
			||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 | 
					"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/admin/tasks.py
 | 
					 | 
				
			||||||
#, python-brace-format
 | 
					 | 
				
			||||||
msgid "New version {version} available!"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/api/schema.py
 | 
					#: authentik/api/schema.py
 | 
				
			||||||
msgid "Generic API Error"
 | 
					msgid "Generic API Error"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
@ -1849,10 +1844,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Used recovery-link to authenticate."
 | 
					msgid "Used recovery-link to authenticate."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/sources/ldap/api.py
 | 
					 | 
				
			||||||
msgid "Only a single LDAP Source with password synchronization is allowed"
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/sources/ldap/models.py
 | 
					#: authentik/sources/ldap/models.py
 | 
				
			||||||
msgid "Server URI"
 | 
					msgid "Server URI"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -19,7 +19,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2024-09-08 00:09+0000\n"
 | 
					"POT-Creation-Date: 2024-08-12 13:45+0000\n"
 | 
				
			||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
					"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
				
			||||||
"Last-Translator: Marc Schmitt, 2024\n"
 | 
					"Last-Translator: Marc Schmitt, 2024\n"
 | 
				
			||||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
 | 
					"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
 | 
				
			||||||
@ -29,11 +29,6 @@ msgstr ""
 | 
				
			|||||||
"Language: fr\n"
 | 
					"Language: fr\n"
 | 
				
			||||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
 | 
					"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/admin/tasks.py
 | 
					 | 
				
			||||||
#, python-brace-format
 | 
					 | 
				
			||||||
msgid "New version {version} available!"
 | 
					 | 
				
			||||||
msgstr "Une nouvelle version {version} est disponible !"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/api/schema.py
 | 
					#: authentik/api/schema.py
 | 
				
			||||||
msgid "Generic API Error"
 | 
					msgid "Generic API Error"
 | 
				
			||||||
msgstr "Erreur d'API Générique"
 | 
					msgstr "Erreur d'API Générique"
 | 
				
			||||||
@ -1347,6 +1342,14 @@ msgstr "Impossible de résoudre l'application"
 | 
				
			|||||||
msgid "DN under which objects are accessible."
 | 
					msgid "DN under which objects are accessible."
 | 
				
			||||||
msgstr "DN sous lequel les objets sont accessibles."
 | 
					msgstr "DN sous lequel les objets sont accessibles."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/providers/ldap/models.py
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"Users in this group can do search queries. If not set, every user can "
 | 
				
			||||||
 | 
					"execute search queries."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Les utilisateurs dans ce groupe peuvent faire des requêtes de recherche. Si "
 | 
				
			||||||
 | 
					"laissé vide, tous les utilisateurs peuvent faire des requêtes de recherche."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/ldap/models.py
 | 
					#: authentik/providers/ldap/models.py
 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
"The start for uidNumbers, this number is added to the user.pk to make sure "
 | 
					"The start for uidNumbers, this number is added to the user.pk to make sure "
 | 
				
			||||||
@ -1393,10 +1396,6 @@ msgstr "Fournisseur LDAP"
 | 
				
			|||||||
msgid "LDAP Providers"
 | 
					msgid "LDAP Providers"
 | 
				
			||||||
msgstr "Fournisseurs LDAP"
 | 
					msgstr "Fournisseurs LDAP"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/ldap/models.py
 | 
					 | 
				
			||||||
msgid "Search full LDAP directory"
 | 
					 | 
				
			||||||
msgstr "Rechercher dans l'annuaire LDAP complet"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/oauth2/id_token.py
 | 
					#: authentik/providers/oauth2/id_token.py
 | 
				
			||||||
msgid "Based on the Hashed User ID"
 | 
					msgid "Based on the Hashed User ID"
 | 
				
			||||||
msgstr "Basé sur le hash de l'ID utilisateur"
 | 
					msgstr "Basé sur le hash de l'ID utilisateur"
 | 
				
			||||||
@ -1797,14 +1796,6 @@ msgstr "Mappage de propriété fournisseur Radius"
 | 
				
			|||||||
msgid "Radius Provider Property Mappings"
 | 
					msgid "Radius Provider Property Mappings"
 | 
				
			||||||
msgstr "Mappages de propriété fournisseur Radius"
 | 
					msgstr "Mappages de propriété fournisseur Radius"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/saml/api/providers.py
 | 
					 | 
				
			||||||
msgid ""
 | 
					 | 
				
			||||||
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
 | 
					 | 
				
			||||||
"Response' must be selected."
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
"Quand une clé de signature est sélectionnée, au moins l'un de « Signer les "
 | 
					 | 
				
			||||||
"assertions » et « Signer les réponses » doit être sélectionné."
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/api/providers.py
 | 
					#: authentik/providers/saml/api/providers.py
 | 
				
			||||||
msgid "Invalid XML Syntax"
 | 
					msgid "Invalid XML Syntax"
 | 
				
			||||||
msgstr "Syntaxe XML Invalide"
 | 
					msgstr "Syntaxe XML Invalide"
 | 
				
			||||||
@ -1953,20 +1944,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Signing Keypair"
 | 
					msgid "Signing Keypair"
 | 
				
			||||||
msgstr "Paire de clés de Signature"
 | 
					msgstr "Paire de clés de Signature"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
 | 
					 | 
				
			||||||
msgid ""
 | 
					 | 
				
			||||||
"When selected, incoming assertions are encrypted by the IdP using the public"
 | 
					 | 
				
			||||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
 | 
					 | 
				
			||||||
"the the private key."
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
"Si activé, les assertions entrantes seront chiffrées par l'IdP avec la clé "
 | 
					 | 
				
			||||||
"publique de la paire de clé de chiffrement. L'assertion est déchiffrée par "
 | 
					 | 
				
			||||||
"le SP en utilisant la clé privée."
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
 | 
					 | 
				
			||||||
msgid "Encryption Keypair"
 | 
					 | 
				
			||||||
msgstr "Paire de clés de chiffrement"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/models.py
 | 
					#: authentik/providers/saml/models.py
 | 
				
			||||||
msgid "Default relay_state value for IDP-initiated logins"
 | 
					msgid "Default relay_state value for IDP-initiated logins"
 | 
				
			||||||
msgstr "Valeur par défaut de relay_state des connexions initiées par l'IdP"
 | 
					msgstr "Valeur par défaut de relay_state des connexions initiées par l'IdP"
 | 
				
			||||||
@ -2489,6 +2466,20 @@ msgstr ""
 | 
				
			|||||||
"Paire de clés utilisées pour signer les réponses sortantes allant vers le "
 | 
					"Paire de clés utilisées pour signer les réponses sortantes allant vers le "
 | 
				
			||||||
"fournisseur d'identité."
 | 
					"fournisseur d'identité."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"When selected, incoming assertions are encrypted by the IdP using the public"
 | 
				
			||||||
 | 
					" key of the encryption keypair. The assertion is decrypted by the SP using "
 | 
				
			||||||
 | 
					"the the private key."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"Si activé, les assertions entrantes seront chiffrées par l'IdP avec la clé "
 | 
				
			||||||
 | 
					"publique de la paire de clé de chiffrement. L'assertion est déchiffrée par "
 | 
				
			||||||
 | 
					"le SP en utilisant la clé privée."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
 | 
					msgid "Encryption Keypair"
 | 
				
			||||||
 | 
					msgstr "Paire de clés de chiffrement"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/sources/saml/models.py
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
msgid "SAML Source"
 | 
					msgid "SAML Source"
 | 
				
			||||||
msgstr "Source SAML"
 | 
					msgstr "Source SAML"
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2024-08-18 00:08+0000\n"
 | 
					"POT-Creation-Date: 2024-08-15 00:09+0000\n"
 | 
				
			||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
					"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
				
			||||||
"Last-Translator: Anton Babenko, 2024\n"
 | 
					"Last-Translator: Anton Babenko, 2024\n"
 | 
				
			||||||
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
 | 
					"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
 | 
				
			||||||
@ -739,7 +739,7 @@ msgstr "Правило Уведомления"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: authentik/events/models.py
 | 
					#: authentik/events/models.py
 | 
				
			||||||
msgid "Notification Rules"
 | 
					msgid "Notification Rules"
 | 
				
			||||||
msgstr "Правила уведомлений"
 | 
					msgstr "Правило Уведомлений"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/events/models.py
 | 
					#: authentik/events/models.py
 | 
				
			||||||
msgid "Webhook Mapping"
 | 
					msgid "Webhook Mapping"
 | 
				
			||||||
@ -1771,14 +1771,6 @@ msgstr "Сопоставление свойства Radius провайдера"
 | 
				
			|||||||
msgid "Radius Provider Property Mappings"
 | 
					msgid "Radius Provider Property Mappings"
 | 
				
			||||||
msgstr "Сопоставление свойств Radius провайдера"
 | 
					msgstr "Сопоставление свойств Radius провайдера"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/saml/api/providers.py
 | 
					 | 
				
			||||||
msgid ""
 | 
					 | 
				
			||||||
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
 | 
					 | 
				
			||||||
"Response' must be selected."
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
"При выборе пары ключей для подписи необходимо выбрать как минимум один из "
 | 
					 | 
				
			||||||
"вариантов: 'Подписывать утверждение' или 'Подписывать ответ'."
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/api/providers.py
 | 
					#: authentik/providers/saml/api/providers.py
 | 
				
			||||||
msgid "Invalid XML Syntax"
 | 
					msgid "Invalid XML Syntax"
 | 
				
			||||||
msgstr "Некорректный синтаксис XML"
 | 
					msgstr "Некорректный синтаксис XML"
 | 
				
			||||||
@ -1926,21 +1918,6 @@ msgstr ""
 | 
				
			|||||||
msgid "Signing Keypair"
 | 
					msgid "Signing Keypair"
 | 
				
			||||||
msgstr "Пара ключей для подписи"
 | 
					msgstr "Пара ключей для подписи"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
 | 
					 | 
				
			||||||
msgid ""
 | 
					 | 
				
			||||||
"When selected, incoming assertions are encrypted by the IdP using the public"
 | 
					 | 
				
			||||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
 | 
					 | 
				
			||||||
"the the private key."
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
"При выборе этого варианта, входящие утверждения шифруются поставщиком "
 | 
					 | 
				
			||||||
"идентификации (IdP) с использованием открытого ключа из пары ключей "
 | 
					 | 
				
			||||||
"шифрования. Утверждение расшифровывается поставщиком услуг (SP) с "
 | 
					 | 
				
			||||||
"использованием закрытого ключа."
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
 | 
					 | 
				
			||||||
msgid "Encryption Keypair"
 | 
					 | 
				
			||||||
msgstr "Пара ключей шифрования"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/providers/saml/models.py
 | 
					#: authentik/providers/saml/models.py
 | 
				
			||||||
msgid "Default relay_state value for IDP-initiated logins"
 | 
					msgid "Default relay_state value for IDP-initiated logins"
 | 
				
			||||||
msgstr "Значение relay_state по умолчанию для логинов, инициированных IDP"
 | 
					msgstr "Значение relay_state по умолчанию для логинов, инициированных IDP"
 | 
				
			||||||
@ -2469,6 +2446,21 @@ msgstr ""
 | 
				
			|||||||
"Пара ключей, используемая для подписи исходящих ответов, направляемых "
 | 
					"Пара ключей, используемая для подписи исходящих ответов, направляемых "
 | 
				
			||||||
"провайдеру идентификационных данных."
 | 
					"провайдеру идентификационных данных."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
 | 
					msgid ""
 | 
				
			||||||
 | 
					"When selected, incoming assertions are encrypted by the IdP using the public"
 | 
				
			||||||
 | 
					" key of the encryption keypair. The assertion is decrypted by the SP using "
 | 
				
			||||||
 | 
					"the the private key."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					"При выборе этого варианта, входящие утверждения шифруются поставщиком "
 | 
				
			||||||
 | 
					"идентификации (IdP) с использованием открытого ключа из пары ключей "
 | 
				
			||||||
 | 
					"шифрования. Утверждение расшифровывается поставщиком услуг (SP) с "
 | 
				
			||||||
 | 
					"использованием закрытого ключа."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
 | 
					msgid "Encryption Keypair"
 | 
				
			||||||
 | 
					msgstr "Пара ключей шифрования"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/sources/saml/models.py
 | 
					#: authentik/sources/saml/models.py
 | 
				
			||||||
msgid "SAML Source"
 | 
					msgid "SAML Source"
 | 
				
			||||||
msgstr "Источник SAML"
 | 
					msgstr "Источник SAML"
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -15,7 +15,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2024-09-25 00:08+0000\n"
 | 
					"POT-Creation-Date: 2024-08-18 00:08+0000\n"
 | 
				
			||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
					"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
				
			||||||
"Last-Translator: deluxghost, 2024\n"
 | 
					"Last-Translator: deluxghost, 2024\n"
 | 
				
			||||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
 | 
					"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
 | 
				
			||||||
@ -25,11 +25,6 @@ msgstr ""
 | 
				
			|||||||
"Language: zh-Hans\n"
 | 
					"Language: zh-Hans\n"
 | 
				
			||||||
"Plural-Forms: nplurals=1; plural=0;\n"
 | 
					"Plural-Forms: nplurals=1; plural=0;\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/admin/tasks.py
 | 
					 | 
				
			||||||
#, python-brace-format
 | 
					 | 
				
			||||||
msgid "New version {version} available!"
 | 
					 | 
				
			||||||
msgstr "新版本 {version} 可用!"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/api/schema.py
 | 
					#: authentik/api/schema.py
 | 
				
			||||||
msgid "Generic API Error"
 | 
					msgid "Generic API Error"
 | 
				
			||||||
msgstr "通用 API 错误"
 | 
					msgstr "通用 API 错误"
 | 
				
			||||||
@ -1882,10 +1877,6 @@ msgstr "创建一个密钥,可用于恢复对 authentik 的访问权限。"
 | 
				
			|||||||
msgid "Used recovery-link to authenticate."
 | 
					msgid "Used recovery-link to authenticate."
 | 
				
			||||||
msgstr "已使用恢复链接进行身份验证。"
 | 
					msgstr "已使用恢复链接进行身份验证。"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/sources/ldap/api.py
 | 
					 | 
				
			||||||
msgid "Only a single LDAP Source with password synchronization is allowed"
 | 
					 | 
				
			||||||
msgstr "仅允许使用密码同步的单个 LDAP 源"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/sources/ldap/models.py
 | 
					#: authentik/sources/ldap/models.py
 | 
				
			||||||
msgid "Server URI"
 | 
					msgid "Server URI"
 | 
				
			||||||
msgstr "服务器 URI"
 | 
					msgstr "服务器 URI"
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -14,7 +14,7 @@ msgid ""
 | 
				
			|||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2024-09-25 00:08+0000\n"
 | 
					"POT-Creation-Date: 2024-08-18 00:08+0000\n"
 | 
				
			||||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
					"PO-Revision-Date: 2022-09-26 16:47+0000\n"
 | 
				
			||||||
"Last-Translator: deluxghost, 2024\n"
 | 
					"Last-Translator: deluxghost, 2024\n"
 | 
				
			||||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
 | 
					"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
 | 
				
			||||||
@ -24,11 +24,6 @@ msgstr ""
 | 
				
			|||||||
"Language: zh_CN\n"
 | 
					"Language: zh_CN\n"
 | 
				
			||||||
"Plural-Forms: nplurals=1; plural=0;\n"
 | 
					"Plural-Forms: nplurals=1; plural=0;\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/admin/tasks.py
 | 
					 | 
				
			||||||
#, python-brace-format
 | 
					 | 
				
			||||||
msgid "New version {version} available!"
 | 
					 | 
				
			||||||
msgstr "新版本 {version} 可用!"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/api/schema.py
 | 
					#: authentik/api/schema.py
 | 
				
			||||||
msgid "Generic API Error"
 | 
					msgid "Generic API Error"
 | 
				
			||||||
msgstr "通用 API 错误"
 | 
					msgstr "通用 API 错误"
 | 
				
			||||||
@ -1881,10 +1876,6 @@ msgstr "创建一个密钥,可用于恢复对 authentik 的访问权限。"
 | 
				
			|||||||
msgid "Used recovery-link to authenticate."
 | 
					msgid "Used recovery-link to authenticate."
 | 
				
			||||||
msgstr "已使用恢复链接进行身份验证。"
 | 
					msgstr "已使用恢复链接进行身份验证。"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/sources/ldap/api.py
 | 
					 | 
				
			||||||
msgid "Only a single LDAP Source with password synchronization is allowed"
 | 
					 | 
				
			||||||
msgstr "仅允许使用密码同步的单个 LDAP 源"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/sources/ldap/models.py
 | 
					#: authentik/sources/ldap/models.py
 | 
				
			||||||
msgid "Server URI"
 | 
					msgid "Server URI"
 | 
				
			||||||
msgstr "服务器 URI"
 | 
					msgstr "服务器 URI"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "@goauthentik/authentik",
 | 
					    "name": "@goauthentik/authentik",
 | 
				
			||||||
    "version": "2024.8.3",
 | 
					    "version": "2024.8.2",
 | 
				
			||||||
    "private": true
 | 
					    "private": true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										569
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										569
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@ -366,13 +366,13 @@ typing-extensions = ">=4.0.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "bandit"
 | 
					name = "bandit"
 | 
				
			||||||
version = "1.7.10"
 | 
					version = "1.7.9"
 | 
				
			||||||
description = "Security oriented static analyser for python code."
 | 
					description = "Security oriented static analyser for python code."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"},
 | 
					    {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"},
 | 
				
			||||||
    {file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"},
 | 
					    {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -1134,49 +1134,46 @@ tests = ["django", "hypothesis", "pytest", "pytest-asyncio"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "debugpy"
 | 
					name = "debugpy"
 | 
				
			||||||
version = "1.8.6"
 | 
					version = "1.8.5"
 | 
				
			||||||
description = "An implementation of the Debug Adapter Protocol for Python"
 | 
					description = "An implementation of the Debug Adapter Protocol for Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b"},
 | 
					    {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b"},
 | 
					    {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9"},
 | 
					    {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd"},
 | 
					    {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955"},
 | 
					    {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b"},
 | 
					    {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43"},
 | 
					    {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833"},
 | 
					    {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128"},
 | 
					    {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972"},
 | 
					    {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c"},
 | 
					    {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f"},
 | 
					    {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb"},
 | 
					    {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a"},
 | 
					    {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8"},
 | 
					    {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d"},
 | 
					    {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa"},
 | 
					    {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881"},
 | 
					    {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123"},
 | 
					    {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51"},
 | 
					    {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"},
 | 
				
			||||||
    {file = "debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f"},
 | 
					    {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"},
 | 
				
			||||||
    {file = "debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a"},
 | 
					    {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "deepmerge"
 | 
					name = "deepmerge"
 | 
				
			||||||
version = "2.0"
 | 
					version = "1.1.1"
 | 
				
			||||||
description = "A toolset for deeply merging Python dictionaries."
 | 
					description = "a toolset to deeply merge python dictionaries."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = "*"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "deepmerge-2.0-py3-none-any.whl", hash = "sha256:6de9ce507115cff0bed95ff0ce9ecc31088ef50cbdf09bc90a09349a318b3d00"},
 | 
					    {file = "deepmerge-1.1.1-py3-none-any.whl", hash = "sha256:7219dad9763f15be9dcd4bcb53e00f48e4eed6f5ed8f15824223eb934bb35977"},
 | 
				
			||||||
    {file = "deepmerge-2.0.tar.gz", hash = "sha256:5c3d86081fbebd04dd5de03626a0607b809a98fb6ccba5770b62466fe940ff20"},
 | 
					    {file = "deepmerge-1.1.1.tar.gz", hash = "sha256:53a489dc9449636e480a784359ae2aab3191748c920649551c8e378622f0eca4"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
dev = ["black", "build", "mypy", "pytest", "pyupgrade", "twine", "validate-pyproject[all]"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "defusedxml"
 | 
					name = "defusedxml"
 | 
				
			||||||
version = "0.7.1"
 | 
					version = "0.7.1"
 | 
				
			||||||
@ -1207,13 +1204,13 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "django"
 | 
					name = "django"
 | 
				
			||||||
version = "5.0.9"
 | 
					version = "5.0.8"
 | 
				
			||||||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
 | 
					description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.10"
 | 
					python-versions = ">=3.10"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "Django-5.0.9-py3-none-any.whl", hash = "sha256:f219576ba53be4e83f485130a7283f0efde06a9f2e3a7c3c5180327549f078fa"},
 | 
					    {file = "Django-5.0.8-py3-none-any.whl", hash = "sha256:333a7988f7ca4bc14d360d3d8f6b793704517761ae3813b95432043daec22a45"},
 | 
				
			||||||
    {file = "Django-5.0.9.tar.gz", hash = "sha256:6333870d342329b60174da3a60dbd302e533f3b0bb0971516750e974a99b5a39"},
 | 
					    {file = "Django-5.0.8.tar.gz", hash = "sha256:ebe859c9da6fead9c9ee6dbfa4943b04f41342f4cea2c4d8c978ef0d10694f2b"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -1287,13 +1284,13 @@ Django = ">=2.2"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "django-model-utils"
 | 
					name = "django-model-utils"
 | 
				
			||||||
version = "5.0.0"
 | 
					version = "4.5.1"
 | 
				
			||||||
description = "Django model mixins and utilities"
 | 
					description = "Django model mixins and utilities"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "django_model_utils-5.0.0-py3-none-any.whl", hash = "sha256:fec78e6c323d565a221f7c4edc703f4567d7bb1caeafe1acd16a80c5ff82056b"},
 | 
					    {file = "django_model_utils-4.5.1-py3-none-any.whl", hash = "sha256:f1141fc71796242edeffed5ad53a8cc57f00d345eb5a3a63e3f69401cd562ee2"},
 | 
				
			||||||
    {file = "django_model_utils-5.0.0.tar.gz", hash = "sha256:041cdd6230d2fbf6cd943e1969318bce762272077f4ecd333ab2263924b4e5eb"},
 | 
					    {file = "django_model_utils-4.5.1.tar.gz", hash = "sha256:1220f22d9a467d53a1e0f4cda4857df0b2f757edf9a29955c42461988caa648a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -1315,13 +1312,13 @@ django = ">=3"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "django-pglock"
 | 
					name = "django-pglock"
 | 
				
			||||||
version = "1.6.2"
 | 
					version = "1.6.0"
 | 
				
			||||||
description = "Postgres locking routines and lock table access."
 | 
					description = "Postgres locking routines and lock table access."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "<4,>=3.8.0"
 | 
					python-versions = "<4,>=3.8.0"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "django_pglock-1.6.2-py3-none-any.whl", hash = "sha256:abdb92531bf8cb36471dc9eb33ed163b06bd3323140d132ed4f308b9e5505f50"},
 | 
					    {file = "django_pglock-1.6.0-py3-none-any.whl", hash = "sha256:41c98d0bd3738d11e6eaefcc3e5146028f118a593ac58c13d663b751170f01de"},
 | 
				
			||||||
    {file = "django_pglock-1.6.2.tar.gz", hash = "sha256:05db998cab21556d4a307eac4b5db8e50f874f42b1a581560b3c54610fee6a1b"},
 | 
					    {file = "django_pglock-1.6.0.tar.gz", hash = "sha256:724450ecc9886f39af599c477d84ad086545a5373215ef7a670cd25faca25a61"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -1566,16 +1563,6 @@ files = [
 | 
				
			|||||||
setuptools = "*"
 | 
					setuptools = "*"
 | 
				
			||||||
six = "*"
 | 
					six = "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "durationpy"
 | 
					 | 
				
			||||||
version = "0.7"
 | 
					 | 
				
			||||||
description = "Module for converting between datetime.timedelta and Go's Duration strings."
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
files = [
 | 
					 | 
				
			||||||
    {file = "durationpy-0.7.tar.gz", hash = "sha256:8447c43df4f1a0b434e70c15a38d77f5c9bd17284bfc1ff1d430f233d5083732"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "email-validator"
 | 
					name = "email-validator"
 | 
				
			||||||
version = "2.2.0"
 | 
					version = "2.2.0"
 | 
				
			||||||
@ -1771,13 +1758,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "google-api-python-client"
 | 
					name = "google-api-python-client"
 | 
				
			||||||
version = "2.147.0"
 | 
					version = "2.142.0"
 | 
				
			||||||
description = "Google API Client Library for Python"
 | 
					description = "Google API Client Library for Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "google_api_python_client-2.147.0-py2.py3-none-any.whl", hash = "sha256:c6ecfa193c695baa41e84562d8f8f244fcd164419eca3fc9fd7565646668f9b2"},
 | 
					    {file = "google_api_python_client-2.142.0-py2.py3-none-any.whl", hash = "sha256:266799082bb8301f423ec204dffbffb470b502abbf29efd1f83e644d36eb5a8f"},
 | 
				
			||||||
    {file = "google_api_python_client-2.147.0.tar.gz", hash = "sha256:e864c2cf61d34c00f05278b8bdb72b93b6fa34f0de9ead51d20435f3b65f91be"},
 | 
					    {file = "google_api_python_client-2.142.0.tar.gz", hash = "sha256:a1101ac9e24356557ca22f07ff48b7f61fa5d4b4e7feeef3bda16e5dcb86350e"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -2060,13 +2047,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "importlib-metadata"
 | 
					name = "importlib-metadata"
 | 
				
			||||||
version = "8.4.0"
 | 
					version = "8.0.0"
 | 
				
			||||||
description = "Read metadata from Python packages"
 | 
					description = "Read metadata from Python packages"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"},
 | 
					    {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
 | 
				
			||||||
    {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"},
 | 
					    {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -2253,18 +2240,17 @@ zookeeper = ["kazoo (>=2.8.0)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "kubernetes"
 | 
					name = "kubernetes"
 | 
				
			||||||
version = "31.0.0"
 | 
					version = "30.1.0"
 | 
				
			||||||
description = "Kubernetes python client"
 | 
					description = "Kubernetes python client"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.6"
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "kubernetes-31.0.0-py2.py3-none-any.whl", hash = "sha256:bf141e2d380c8520eada8b351f4e319ffee9636328c137aa432bc486ca1200e1"},
 | 
					    {file = "kubernetes-30.1.0-py2.py3-none-any.whl", hash = "sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d"},
 | 
				
			||||||
    {file = "kubernetes-31.0.0.tar.gz", hash = "sha256:28945de906c8c259c1ebe62703b56a03b714049372196f854105afe4e6d014c0"},
 | 
					    {file = "kubernetes-30.1.0.tar.gz", hash = "sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
certifi = ">=14.05.14"
 | 
					certifi = ">=14.05.14"
 | 
				
			||||||
durationpy = ">=0.7"
 | 
					 | 
				
			||||||
google-auth = ">=1.0.1"
 | 
					google-auth = ">=1.0.1"
 | 
				
			||||||
oauthlib = ">=3.2.2"
 | 
					oauthlib = ">=3.2.2"
 | 
				
			||||||
python-dateutil = ">=2.5.3"
 | 
					python-dateutil = ">=2.5.3"
 | 
				
			||||||
@ -2859,13 +2845,13 @@ dev = ["bumpver", "isort", "mypy", "pylint", "pytest", "yapf"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "msgraph-sdk"
 | 
					name = "msgraph-sdk"
 | 
				
			||||||
version = "1.8.0"
 | 
					version = "1.5.4"
 | 
				
			||||||
description = "The Microsoft Graph Python SDK"
 | 
					description = "The Microsoft Graph Python SDK"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "msgraph_sdk-1.8.0-py3-none-any.whl", hash = "sha256:22a8e4a63f989865228f66a54501bef8105909c7156fe0a079ca9b5296339cc2"},
 | 
					    {file = "msgraph_sdk-1.5.4-py3-none-any.whl", hash = "sha256:9ea349f30cc4a03edb587e26554c7a4839a38c2ef30d4b5396882fd2be82dcac"},
 | 
				
			||||||
    {file = "msgraph_sdk-1.8.0.tar.gz", hash = "sha256:1ac84bd47ea288a84f46f6c6d0c89d164ee3453b917615632652344538098314"},
 | 
					    {file = "msgraph_sdk-1.5.4.tar.gz", hash = "sha256:b0e146328d136d1db175938d8fc901f3bb32acf3ea6fe93c0dc7c5a0abc45e39"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3039,49 +3025,49 @@ resolved_reference = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "opentelemetry-api"
 | 
					name = "opentelemetry-api"
 | 
				
			||||||
version = "1.27.0"
 | 
					version = "1.26.0"
 | 
				
			||||||
description = "OpenTelemetry Python API"
 | 
					description = "OpenTelemetry Python API"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"},
 | 
					    {file = "opentelemetry_api-1.26.0-py3-none-any.whl", hash = "sha256:7d7ea33adf2ceda2dd680b18b1677e4152000b37ca76e679da71ff103b943064"},
 | 
				
			||||||
    {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"},
 | 
					    {file = "opentelemetry_api-1.26.0.tar.gz", hash = "sha256:2bd639e4bed5b18486fef0b5a520aaffde5a18fc225e808a1ac4df363f43a1ce"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
deprecated = ">=1.2.6"
 | 
					deprecated = ">=1.2.6"
 | 
				
			||||||
importlib-metadata = ">=6.0,<=8.4.0"
 | 
					importlib-metadata = ">=6.0,<=8.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "opentelemetry-sdk"
 | 
					name = "opentelemetry-sdk"
 | 
				
			||||||
version = "1.27.0"
 | 
					version = "1.26.0"
 | 
				
			||||||
description = "OpenTelemetry Python SDK"
 | 
					description = "OpenTelemetry Python SDK"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"},
 | 
					    {file = "opentelemetry_sdk-1.26.0-py3-none-any.whl", hash = "sha256:feb5056a84a88670c041ea0ded9921fca559efec03905dddeb3885525e0af897"},
 | 
				
			||||||
    {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"},
 | 
					    {file = "opentelemetry_sdk-1.26.0.tar.gz", hash = "sha256:c90d2868f8805619535c05562d699e2f4fb1f00dbd55a86dcefca4da6fa02f85"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
opentelemetry-api = "1.27.0"
 | 
					opentelemetry-api = "1.26.0"
 | 
				
			||||||
opentelemetry-semantic-conventions = "0.48b0"
 | 
					opentelemetry-semantic-conventions = "0.47b0"
 | 
				
			||||||
typing-extensions = ">=3.7.4"
 | 
					typing-extensions = ">=3.7.4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "opentelemetry-semantic-conventions"
 | 
					name = "opentelemetry-semantic-conventions"
 | 
				
			||||||
version = "0.48b0"
 | 
					version = "0.47b0"
 | 
				
			||||||
description = "OpenTelemetry Semantic Conventions"
 | 
					description = "OpenTelemetry Semantic Conventions"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"},
 | 
					    {file = "opentelemetry_semantic_conventions-0.47b0-py3-none-any.whl", hash = "sha256:4ff9d595b85a59c1c1413f02bba320ce7ea6bf9e2ead2b0913c4395c7bbc1063"},
 | 
				
			||||||
    {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"},
 | 
					    {file = "opentelemetry_semantic_conventions-0.47b0.tar.gz", hash = "sha256:a8d57999bbe3495ffd4d510de26a97dadc1dace53e0275001b2c1b2f67992a7e"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
deprecated = ">=1.2.6"
 | 
					deprecated = ">=1.2.6"
 | 
				
			||||||
opentelemetry-api = "1.27.0"
 | 
					opentelemetry-api = "1.26.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "orjson"
 | 
					name = "orjson"
 | 
				
			||||||
@ -3172,13 +3158,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "paramiko"
 | 
					name = "paramiko"
 | 
				
			||||||
version = "3.5.0"
 | 
					version = "3.4.1"
 | 
				
			||||||
description = "SSH2 protocol library"
 | 
					description = "SSH2 protocol library"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.6"
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "paramiko-3.5.0-py3-none-any.whl", hash = "sha256:1fedf06b085359051cd7d0d270cebe19e755a8a921cc2ddbfa647fb0cd7d68f9"},
 | 
					    {file = "paramiko-3.4.1-py3-none-any.whl", hash = "sha256:8e49fd2f82f84acf7ffd57c64311aa2b30e575370dc23bdb375b10262f7eac32"},
 | 
				
			||||||
    {file = "paramiko-3.5.0.tar.gz", hash = "sha256:ad11e540da4f55cedda52931f1a3f812a8238a7af7f62a60de538cd80bb28124"},
 | 
					    {file = "paramiko-3.4.1.tar.gz", hash = "sha256:8b15302870af7f6652f2e038975c1d2973f06046cb5d7d65355668b3ecbece0c"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3215,13 +3201,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pdoc"
 | 
					name = "pdoc"
 | 
				
			||||||
version = "14.7.0"
 | 
					version = "14.6.0"
 | 
				
			||||||
description = "API Documentation for Python Projects"
 | 
					description = "API Documentation for Python Projects"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pdoc-14.7.0-py3-none-any.whl", hash = "sha256:72377a907efc6b2c5b3c56b717ef34f11d93621dced3b663f3aede0b844c0ad2"},
 | 
					    {file = "pdoc-14.6.0-py3-none-any.whl", hash = "sha256:36c42c546a317d8e3e8c0b39645f24161374de0c7066ccaae76628d721e49ba5"},
 | 
				
			||||||
    {file = "pdoc-14.7.0.tar.gz", hash = "sha256:2d28af9c0acc39180744ad0543e4bbc3223ecba0d1302db315ec521c51f71f93"},
 | 
					    {file = "pdoc-14.6.0.tar.gz", hash = "sha256:6e98a24c5e0ca5d188397969cf82581836eaef13f172fc3820047bfe15c61c9a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3448,36 +3434,36 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "psycopg"
 | 
					name = "psycopg"
 | 
				
			||||||
version = "3.2.3"
 | 
					version = "3.2.1"
 | 
				
			||||||
description = "PostgreSQL database adapter for Python"
 | 
					description = "PostgreSQL database adapter for Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "psycopg-3.2.3-py3-none-any.whl", hash = "sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907"},
 | 
					    {file = "psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175"},
 | 
				
			||||||
    {file = "psycopg-3.2.3.tar.gz", hash = "sha256:a5764f67c27bec8bfac85764d23c534af2c27b893550377e37ce59c12aac47a2"},
 | 
					    {file = "psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
psycopg-c = {version = "3.2.3", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""}
 | 
					psycopg-c = {version = "3.2.1", optional = true, markers = "implementation_name != \"pypy\" and extra == \"c\""}
 | 
				
			||||||
typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""}
 | 
					typing-extensions = ">=4.4"
 | 
				
			||||||
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 | 
					tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
binary = ["psycopg-binary (==3.2.3)"]
 | 
					binary = ["psycopg-binary (==3.2.1)"]
 | 
				
			||||||
c = ["psycopg-c (==3.2.3)"]
 | 
					c = ["psycopg-c (==3.2.1)"]
 | 
				
			||||||
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.11)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
 | 
					dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.6)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
 | 
				
			||||||
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
 | 
					docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
 | 
				
			||||||
pool = ["psycopg-pool"]
 | 
					pool = ["psycopg-pool"]
 | 
				
			||||||
test = ["anyio (>=4.0)", "mypy (>=1.11)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
 | 
					test = ["anyio (>=4.0)", "mypy (>=1.6)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "psycopg-c"
 | 
					name = "psycopg-c"
 | 
				
			||||||
version = "3.2.3"
 | 
					version = "3.2.1"
 | 
				
			||||||
description = "PostgreSQL database adapter for Python -- C optimisation distribution"
 | 
					description = "PostgreSQL database adapter for Python -- C optimisation distribution"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "psycopg_c-3.2.3.tar.gz", hash = "sha256:06ae7db8eaec1a3845960fa7f997f4ccdb1a7a7ab8dc593a680bcc74e1359671"},
 | 
					    {file = "psycopg_c-3.2.1.tar.gz", hash = "sha256:2d09943cc8a855c42c1e23b4298957b7ce8f27bf3683258c52fd139f601f7cda"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -3518,121 +3504,120 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pydantic"
 | 
					name = "pydantic"
 | 
				
			||||||
version = "2.9.2"
 | 
					version = "2.8.2"
 | 
				
			||||||
description = "Data validation using Python type hints"
 | 
					description = "Data validation using Python type hints"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
 | 
					    {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
 | 
				
			||||||
    {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
 | 
					    {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
annotated-types = ">=0.6.0"
 | 
					annotated-types = ">=0.4.0"
 | 
				
			||||||
email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""}
 | 
					email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""}
 | 
				
			||||||
pydantic-core = "2.23.4"
 | 
					pydantic-core = "2.20.1"
 | 
				
			||||||
typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""}
 | 
					typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
email = ["email-validator (>=2.0.0)"]
 | 
					email = ["email-validator (>=2.0.0)"]
 | 
				
			||||||
timezone = ["tzdata"]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pydantic-core"
 | 
					name = "pydantic-core"
 | 
				
			||||||
version = "2.23.4"
 | 
					version = "2.20.1"
 | 
				
			||||||
description = "Core functionality for Pydantic validation and serialization"
 | 
					description = "Core functionality for Pydantic validation and serialization"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"},
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
 | 
				
			||||||
    {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
 | 
					    {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3776,13 +3761,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pytest"
 | 
					name = "pytest"
 | 
				
			||||||
version = "8.3.3"
 | 
					version = "8.3.2"
 | 
				
			||||||
description = "pytest: simple powerful testing with Python"
 | 
					description = "pytest: simple powerful testing with Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"},
 | 
					    {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
 | 
				
			||||||
    {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"},
 | 
					    {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3796,13 +3781,13 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pytest-django"
 | 
					name = "pytest-django"
 | 
				
			||||||
version = "4.9.0"
 | 
					version = "4.8.0"
 | 
				
			||||||
description = "A Django plugin for pytest."
 | 
					description = "A Django plugin for pytest."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "pytest_django-4.9.0-py3-none-any.whl", hash = "sha256:1d83692cb39188682dbb419ff0393867e9904094a549a7d38a3154d5731b2b99"},
 | 
					    {file = "pytest-django-4.8.0.tar.gz", hash = "sha256:5d054fe011c56f3b10f978f41a8efb2e5adfc7e680ef36fb571ada1f24779d90"},
 | 
				
			||||||
    {file = "pytest_django-4.9.0.tar.gz", hash = "sha256:8bf7bc358c9ae6f6fc51b6cebb190fe20212196e6807121f11bd6a3b03428314"},
 | 
					    {file = "pytest_django-4.8.0-py3-none-any.whl", hash = "sha256:ca1ddd1e0e4c227cf9e3e40a6afc6d106b3e70868fd2ac5798a22501271cd0c7"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4210,29 +4195,29 @@ pyasn1 = ">=0.1.3"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "ruff"
 | 
					name = "ruff"
 | 
				
			||||||
version = "0.6.8"
 | 
					version = "0.6.2"
 | 
				
			||||||
description = "An extremely fast Python linter and code formatter, written in Rust."
 | 
					description = "An extremely fast Python linter and code formatter, written in Rust."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.7"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"},
 | 
					    {file = "ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"},
 | 
					    {file = "ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"},
 | 
					    {file = "ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"},
 | 
					    {file = "ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"},
 | 
					    {file = "ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"},
 | 
					    {file = "ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"},
 | 
					    {file = "ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"},
 | 
					    {file = "ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"},
 | 
					    {file = "ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"},
 | 
					    {file = "ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2"},
 | 
				
			||||||
    {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"},
 | 
					    {file = "ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9"},
 | 
				
			||||||
    {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"},
 | 
					    {file = "ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -4271,13 +4256,13 @@ django-query = ["django (>=3.2)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "selenium"
 | 
					name = "selenium"
 | 
				
			||||||
version = "4.25.0"
 | 
					version = "4.23.1"
 | 
				
			||||||
description = "Official Python bindings for Selenium WebDriver"
 | 
					description = "Official Python bindings for Selenium WebDriver"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "selenium-4.25.0-py3-none-any.whl", hash = "sha256:3798d2d12b4a570bc5790163ba57fef10b2afee958bf1d80f2a3cf07c4141f33"},
 | 
					    {file = "selenium-4.23.1-py3-none-any.whl", hash = "sha256:3a8d9f23dc636bd3840dd56f00c2739e32ec0c1e34a821dd553e15babef24477"},
 | 
				
			||||||
    {file = "selenium-4.25.0.tar.gz", hash = "sha256:95d08d3b82fb353f3c474895154516604c7f0e6a9a565ae6498ef36c9bac6921"},
 | 
					    {file = "selenium-4.23.1.tar.gz", hash = "sha256:128d099e66284437e7128d2279176ec7a06e6ec7426e167f5d34987166bd8f46"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4290,13 +4275,13 @@ websocket-client = ">=1.8,<2.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "sentry-sdk"
 | 
					name = "sentry-sdk"
 | 
				
			||||||
version = "2.14.0"
 | 
					version = "2.13.0"
 | 
				
			||||||
description = "Python client for Sentry (https://sentry.io)"
 | 
					description = "Python client for Sentry (https://sentry.io)"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.6"
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "sentry_sdk-2.14.0-py2.py3-none-any.whl", hash = "sha256:b8bc3dc51d06590df1291b7519b85c75e2ced4f28d9ea655b6d54033503b5bf4"},
 | 
					    {file = "sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6"},
 | 
				
			||||||
    {file = "sentry_sdk-2.14.0.tar.gz", hash = "sha256:1e0e2eaf6dad918c7d1e0edac868a7bf20017b177f242cefe2a6bcd47955961d"},
 | 
					    {file = "sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4667,13 +4652,13 @@ wsproto = ">=0.14"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "twilio"
 | 
					name = "twilio"
 | 
				
			||||||
version = "9.3.2"
 | 
					version = "9.2.3"
 | 
				
			||||||
description = "Twilio API client and TwiML generator"
 | 
					description = "Twilio API client and TwiML generator"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7.0"
 | 
					python-versions = ">=3.7.0"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "twilio-9.3.2-py2.py3-none-any.whl", hash = "sha256:7fcb2da241d2264b17fbab9ac0ca829c0f0abe23ce6db15d4bb0d4d2d583f953"},
 | 
					    {file = "twilio-9.2.3-py2.py3-none-any.whl", hash = "sha256:76bfc39aa8d854510907cb7f9465814dfdea9e91ec199bb44f0785f05746f4cc"},
 | 
				
			||||||
    {file = "twilio-9.3.2.tar.gz", hash = "sha256:250fc6ce6960aa97a2e2ee7e718e3bc0e73d69731b97fe160ed2097f3cbeb5a8"},
 | 
					    {file = "twilio-9.2.3.tar.gz", hash = "sha256:da2255b5f3753cb3bf647fc6c50edbdb367ebc3cde6802806f6f863058a65f75"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4684,13 +4669,13 @@ requests = ">=2.0.0"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "twisted"
 | 
					name = "twisted"
 | 
				
			||||||
version = "24.7.0"
 | 
					version = "24.3.0"
 | 
				
			||||||
description = "An asynchronous networking framework written in Python"
 | 
					description = "An asynchronous networking framework written in Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8.0"
 | 
					python-versions = ">=3.8.0"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "twisted-24.7.0-py3-none-any.whl", hash = "sha256:734832ef98108136e222b5230075b1079dad8a3fc5637319615619a7725b0c81"},
 | 
					    {file = "twisted-24.3.0-py3-none-any.whl", hash = "sha256:039f2e6a49ab5108abd94de187fa92377abe5985c7a72d68d0ad266ba19eae63"},
 | 
				
			||||||
    {file = "twisted-24.7.0.tar.gz", hash = "sha256:5a60147f044187a127ec7da96d170d49bcce50c6fd36f594e60f4587eff4d394"},
 | 
					    {file = "twisted-24.3.0.tar.gz", hash = "sha256:6b38b6ece7296b5e122c9eb17da2eeab3d98a198f50ca9efd00fb03e5b4fd4ae"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4699,26 +4684,55 @@ automat = ">=0.8.0"
 | 
				
			|||||||
constantly = ">=15.1"
 | 
					constantly = ">=15.1"
 | 
				
			||||||
hyperlink = ">=17.1.1"
 | 
					hyperlink = ">=17.1.1"
 | 
				
			||||||
idna = {version = ">=2.4", optional = true, markers = "extra == \"tls\""}
 | 
					idna = {version = ">=2.4", optional = true, markers = "extra == \"tls\""}
 | 
				
			||||||
incremental = ">=24.7.0"
 | 
					incremental = ">=22.10.0"
 | 
				
			||||||
pyopenssl = {version = ">=21.0.0", optional = true, markers = "extra == \"tls\""}
 | 
					pyopenssl = {version = ">=21.0.0", optional = true, markers = "extra == \"tls\""}
 | 
				
			||||||
service-identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""}
 | 
					service-identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""}
 | 
				
			||||||
 | 
					twisted-iocpsupport = {version = ">=1.0.2,<2", markers = "platform_system == \"Windows\""}
 | 
				
			||||||
typing-extensions = ">=4.2.0"
 | 
					typing-extensions = ">=4.2.0"
 | 
				
			||||||
zope-interface = ">=5"
 | 
					zope-interface = ">=5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
all-non-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
 | 
					all-non-platform = ["twisted[conch,http2,serial,test,tls]", "twisted[conch,http2,serial,test,tls]"]
 | 
				
			||||||
conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"]
 | 
					conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"]
 | 
				
			||||||
dev = ["coverage (>=7.5,<8.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "python-subunit (>=1.4,<2.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)"]
 | 
					dev = ["coverage (>=6b1,<7)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "twisted[dev-release]", "twistedchecker (>=0.7,<1.0)"]
 | 
				
			||||||
dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"]
 | 
					dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"]
 | 
				
			||||||
gtk-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pygobject", "pygobject", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
 | 
					gtk-platform = ["pygobject", "pygobject", "twisted[all-non-platform]", "twisted[all-non-platform]"]
 | 
				
			||||||
http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"]
 | 
					http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"]
 | 
				
			||||||
macos-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
 | 
					macos-platform = ["pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "twisted[all-non-platform]", "twisted[all-non-platform]"]
 | 
				
			||||||
mypy = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "coverage (>=7.5,<8.0)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "idna (>=2.4)", "mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "priority (>=1.1.0,<2.0)", "pydoctor (>=23.9.0,<23.10.0)", "pyflakes (>=2.2,<3.0)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "twistedchecker (>=0.7,<1.0)", "types-pyopenssl", "types-setuptools"]
 | 
					mypy = ["mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "twisted[all-non-platform,dev]", "types-pyopenssl", "types-setuptools"]
 | 
				
			||||||
osx-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)"]
 | 
					osx-platform = ["twisted[macos-platform]", "twisted[macos-platform]"]
 | 
				
			||||||
serial = ["pyserial (>=3.0)", "pywin32 (!=226)"]
 | 
					serial = ["pyserial (>=3.0)", "pywin32 (!=226)"]
 | 
				
			||||||
test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"]
 | 
					test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"]
 | 
				
			||||||
tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"]
 | 
					tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"]
 | 
				
			||||||
windows-platform = ["appdirs (>=1.4.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)", "cryptography (>=3.3)", "cython-test-exception-raiser (>=1.0.2,<2)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.56)", "hypothesis (>=6.56)", "idna (>=2.4)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "priority (>=1.1.0,<2.0)", "pyhamcrest (>=2)", "pyhamcrest (>=2)", "pyopenssl (>=21.0.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)", "service-identity (>=18.1.0)", "twisted-iocpsupport (>=1.0.2)", "twisted-iocpsupport (>=1.0.2)"]
 | 
					windows-platform = ["pywin32 (!=226)", "pywin32 (!=226)", "twisted[all-non-platform]", "twisted[all-non-platform]"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "twisted-iocpsupport"
 | 
				
			||||||
 | 
					version = "1.0.4"
 | 
				
			||||||
 | 
					description = "An extension for use in the twisted I/O Completion Ports reactor."
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = "*"
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "twisted-iocpsupport-1.0.4.tar.gz", hash = "sha256:858096c0d15e33f15ac157f455d8f86f2f2cdd223963e58c0f682a3af8362d89"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp310-cp310-win32.whl", hash = "sha256:afa2b630797f9ed2f27f3d9f55e3f72b4244911e45a8c82756f44babbf0b243e"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:0058c963c8957bcd3deda62122e89953c9de1e867a274facc9b15dde1a9f31e8"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp311-cp311-win32.whl", hash = "sha256:196f7c7ccad4ba4d1783b1c4e1d1b22d93c04275cd780bf7498d16c77319ad6e"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:4e5f97bcbabdd79cbaa969b63439b89801ea560f11d42b0a387634275c633623"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp312-cp312-win32.whl", hash = "sha256:6081bd7c2f4fcf9b383dcdb3b3385d75a26a7c9d2be25b6950c3d8ea652d2d2d"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:76f7e67cec1f1d097d1f4ed7de41be3d74546e1a4ede0c7d56e775c4dce5dfb0"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:3d306fc4d88a6bcf61ce9d572c738b918578121bfd72891625fab314549024b5"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:391ac4d6002a80e15f35adc4ad6056f4fe1c17ceb0d1f98ba01b0f4f917adfd7"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:0c1b5cf37f0b2d96cc3c9bc86fff16613b9f5d0ca565c96cf1f1fb8cfca4b81c"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:3c5dc11d72519e55f727320e3cee535feedfaee09c0f0765ed1ca7badff1ab3c"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp38-cp38-win32.whl", hash = "sha256:cc86c2ef598c15d824a243c2541c29459881c67fc3c0adb6efe2242f8f0ec3af"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c27985e949b9b1a1fb4c20c71d315c10ea0f93fdf3ccdd4a8c158b5926edd8c8"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp39-cp39-win32.whl", hash = "sha256:e311dfcb470696e3c077249615893cada598e62fa7c4e4ca090167bd2b7d331f"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4574eef1f3bb81501fb02f911298af3c02fe8179c31a33b361dd49180c3e644d"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:872747a3b64e2909aee59c803ccd0bceb9b75bf27915520ebd32d69687040fa2"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:c2712b778bacf1db434e3e065adfed3db300754186a29aecac1efae9ef4bcaff"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7c66fa0aa4236b27b3c61cb488662d85dae746a6d1c7b0d91cf7aae118445adf"},
 | 
				
			||||||
 | 
					    {file = "twisted_iocpsupport-1.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:300437af17396a945a58dcfffd77863303a8b6d9e65c6e81f1d2eed55b50d444"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "txaio"
 | 
					name = "txaio"
 | 
				
			||||||
@ -4782,13 +4796,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "urllib3"
 | 
					name = "urllib3"
 | 
				
			||||||
version = "2.2.3"
 | 
					version = "2.2.2"
 | 
				
			||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
 | 
					description = "HTTP library with thread-safe connection pooling, file post, and more."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
 | 
					    {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
 | 
				
			||||||
    {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
 | 
					    {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4802,13 +4816,13 @@ zstd = ["zstandard (>=0.18.0)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "uvicorn"
 | 
					name = "uvicorn"
 | 
				
			||||||
version = "0.31.0"
 | 
					version = "0.30.6"
 | 
				
			||||||
description = "The lightning-fast ASGI server."
 | 
					description = "The lightning-fast ASGI server."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "uvicorn-0.31.0-py3-none-any.whl", hash = "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced"},
 | 
					    {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"},
 | 
				
			||||||
    {file = "uvicorn-0.31.0.tar.gz", hash = "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906"},
 | 
					    {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4882,41 +4896,46 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "watchdog"
 | 
					name = "watchdog"
 | 
				
			||||||
version = "5.0.3"
 | 
					version = "4.0.2"
 | 
				
			||||||
description = "Filesystem events monitoring"
 | 
					description = "Filesystem events monitoring"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.9"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:85527b882f3facda0579bce9d743ff7f10c3e1e0db0a0d0e28170a7d0e5ce2ea"},
 | 
					    {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:53adf73dcdc0ef04f7735066b4a57a4cd3e49ef135daae41d77395f0b5b692cb"},
 | 
					    {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e25adddab85f674acac303cf1f5835951345a56c5f7f582987d266679979c75b"},
 | 
					    {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f01f4a3565a387080dc49bdd1fefe4ecc77f894991b88ef927edbfa45eb10818"},
 | 
					    {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91b522adc25614cdeaf91f7897800b82c13b4b8ac68a42ca959f992f6990c490"},
 | 
					    {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d52db5beb5e476e6853da2e2d24dbbbed6797b449c8bf7ea118a4ee0d2c9040e"},
 | 
					    {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d11b07c64f63f49876e0ab8042ae034674c8653bfcdaa8c4b32e71cfff87e8"},
 | 
					    {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:349c9488e1d85d0a58e8cb14222d2c51cbc801ce11ac3936ab4c3af986536926"},
 | 
					    {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:53a3f10b62c2d569e260f96e8d966463dec1a50fa4f1b22aec69e3f91025060e"},
 | 
					    {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:950f531ec6e03696a2414b6308f5c6ff9dab7821a768c9d5788b1314e9a46ca7"},
 | 
					    {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae6deb336cba5d71476caa029ceb6e88047fc1dc74b62b7c4012639c0b563906"},
 | 
					    {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1021223c08ba8d2d38d71ec1704496471ffd7be42cfb26b87cd5059323a389a1"},
 | 
					    {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:752fb40efc7cc8d88ebc332b8f4bcbe2b5cc7e881bccfeb8e25054c00c994ee3"},
 | 
					    {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2e8f3f955d68471fa37b0e3add18500790d129cc7efe89971b8a4cc6fdeb0b2"},
 | 
					    {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b8ca4d854adcf480bdfd80f46fdd6fb49f91dd020ae11c89b3a79e19454ec627"},
 | 
					    {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:90a67d7857adb1d985aca232cc9905dd5bc4803ed85cfcdcfcf707e52049eda7"},
 | 
					    {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:720ef9d3a4f9ca575a780af283c8fd3a0674b307651c1976714745090da5a9e8"},
 | 
					    {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:223160bb359281bb8e31c8f1068bf71a6b16a8ad3d9524ca6f523ac666bb6a1e"},
 | 
					    {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:560135542c91eaa74247a2e8430cf83c4342b29e8ad4f520ae14f0c8a19cfb5b"},
 | 
					    {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dd021efa85970bd4824acacbb922066159d0f9e546389a4743d56919b6758b91"},
 | 
					    {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_armv7l.whl", hash = "sha256:78864cc8f23dbee55be34cc1494632a7ba30263951b5b2e8fc8286b95845f82c"},
 | 
					    {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_i686.whl", hash = "sha256:1e9679245e3ea6498494b3028b90c7b25dbb2abe65c7d07423ecfc2d6218ff7c"},
 | 
					    {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64.whl", hash = "sha256:9413384f26b5d050b6978e6fcd0c1e7f0539be7a4f1a885061473c5deaa57221"},
 | 
					    {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:294b7a598974b8e2c6123d19ef15de9abcd282b0fbbdbc4d23dfa812959a9e05"},
 | 
					    {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:26dd201857d702bdf9d78c273cafcab5871dd29343748524695cecffa44a8d97"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:0f9332243355643d567697c3e3fa07330a1d1abf981611654a1f2bf2175612b7"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-win32.whl", hash = "sha256:c66f80ee5b602a9c7ab66e3c9f36026590a0902db3aea414d59a2f55188c1f49"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-win_amd64.whl", hash = "sha256:f00b4cf737f568be9665563347a910f8bdc76f88c2970121c86243c8cfdf90e9"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"},
 | 
				
			||||||
    {file = "watchdog-5.0.3-py3-none-win_ia64.whl", hash = "sha256:49f4d36cb315c25ea0d946e018c01bb028048023b9e103d3d3943f58e109dd45"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"},
 | 
				
			||||||
    {file = "watchdog-5.0.3.tar.gz", hash = "sha256:108f42a7f0345042a854d4d0ad0834b741d421330d5f575b81cb27b883500176"},
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"},
 | 
				
			||||||
 | 
					    {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"},
 | 
				
			||||||
 | 
					    {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"},
 | 
				
			||||||
 | 
					    {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"},
 | 
				
			||||||
 | 
					    {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"},
 | 
				
			||||||
 | 
					    {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
[tool.poetry]
 | 
					[tool.poetry]
 | 
				
			||||||
name = "authentik"
 | 
					name = "authentik"
 | 
				
			||||||
version = "2024.8.3"
 | 
					version = "2024.8.2"
 | 
				
			||||||
description = ""
 | 
					description = ""
 | 
				
			||||||
authors = ["authentik Team <hello@goauthentik.io>"]
 | 
					authors = ["authentik Team <hello@goauthentik.io>"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
openapi: 3.0.3
 | 
					openapi: 3.0.3
 | 
				
			||||||
info:
 | 
					info:
 | 
				
			||||||
  title: authentik
 | 
					  title: authentik
 | 
				
			||||||
  version: 2024.8.3
 | 
					  version: 2024.8.2
 | 
				
			||||||
  description: Making authentication simple.
 | 
					  description: Making authentication simple.
 | 
				
			||||||
  contact:
 | 
					  contact:
 | 
				
			||||||
    email: hello@goauthentik.io
 | 
					    email: hello@goauthentik.io
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								tests/wdio/.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/wdio/.eslintignore
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					# don't ever lint node_modules
 | 
				
			||||||
 | 
					node_modules
 | 
				
			||||||
 | 
					# don't lint nyc coverage output
 | 
				
			||||||
 | 
					coverage
 | 
				
			||||||
							
								
								
									
										29
									
								
								tests/wdio/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/wdio/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "env": {
 | 
				
			||||||
 | 
					        "browser": true,
 | 
				
			||||||
 | 
					        "es2021": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
 | 
				
			||||||
 | 
					    "parser": "@typescript-eslint/parser",
 | 
				
			||||||
 | 
					    "parserOptions": {
 | 
				
			||||||
 | 
					        "ecmaVersion": 12,
 | 
				
			||||||
 | 
					        "sourceType": "module"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "plugins": ["@typescript-eslint"],
 | 
				
			||||||
 | 
					    "rules": {
 | 
				
			||||||
 | 
					        "indent": "off",
 | 
				
			||||||
 | 
					        "linebreak-style": ["error", "unix"],
 | 
				
			||||||
 | 
					        "quotes": ["error", "double", { "avoidEscape": true }],
 | 
				
			||||||
 | 
					        "semi": ["error", "always"],
 | 
				
			||||||
 | 
					        "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
 | 
					        "no-unused-vars": "off",
 | 
				
			||||||
 | 
					        "@typescript-eslint/no-unused-vars": [
 | 
				
			||||||
 | 
					            "error",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "argsIgnorePattern": "^_",
 | 
				
			||||||
 | 
					                "varsIgnorePattern": "^_",
 | 
				
			||||||
 | 
					                "caughtErrorsIgnorePattern": "^_"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								tests/wdio/.eslintrc.precommit.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tests/wdio/.eslintrc.precommit.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "env": {
 | 
				
			||||||
 | 
					        "browser": true,
 | 
				
			||||||
 | 
					        "es2021": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extends": [
 | 
				
			||||||
 | 
					        "eslint:recommended",
 | 
				
			||||||
 | 
					        "plugin:@typescript-eslint/recommended",
 | 
				
			||||||
 | 
					        "plugin:sonarjs/recommended"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "parser": "@typescript-eslint/parser",
 | 
				
			||||||
 | 
					    "parserOptions": {
 | 
				
			||||||
 | 
					        "ecmaVersion": 12,
 | 
				
			||||||
 | 
					        "sourceType": "module"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "plugins": ["@typescript-eslint", "sonarjs"],
 | 
				
			||||||
 | 
					    "rules": {
 | 
				
			||||||
 | 
					        "indent": "off",
 | 
				
			||||||
 | 
					        "linebreak-style": ["error", "unix"],
 | 
				
			||||||
 | 
					        "quotes": ["error", "double", { "avoidEscape": true }],
 | 
				
			||||||
 | 
					        "semi": ["error", "always"],
 | 
				
			||||||
 | 
					        "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
 | 
					        "sonarjs/cognitive-complexity": ["error", 9],
 | 
				
			||||||
 | 
					        "sonarjs/no-nested-template-literals": "off"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,84 +0,0 @@
 | 
				
			|||||||
import eslint from "@eslint/js";
 | 
					 | 
				
			||||||
import tsparser from "@typescript-eslint/parser";
 | 
					 | 
				
			||||||
import litconf from "eslint-plugin-lit";
 | 
					 | 
				
			||||||
import wcconf from "eslint-plugin-wc";
 | 
					 | 
				
			||||||
import globals from "globals";
 | 
					 | 
				
			||||||
import tseslint from "typescript-eslint";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default [
 | 
					 | 
				
			||||||
    // You would not believe how much this change has frustrated users: ["if an ignores key is used
 | 
					 | 
				
			||||||
    // without any other keys in the configuration object, then the patterns act as global
 | 
					 | 
				
			||||||
    // ignores"](https://eslint.org/docs/latest/use/configure/ignore)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        ignores: [
 | 
					 | 
				
			||||||
            "dist/",
 | 
					 | 
				
			||||||
            // don't lint the cache
 | 
					 | 
				
			||||||
            ".wireit/",
 | 
					 | 
				
			||||||
            // let packages have their own configurations
 | 
					 | 
				
			||||||
            "packages/",
 | 
					 | 
				
			||||||
            // don't ever lint node_modules
 | 
					 | 
				
			||||||
            "node_modules/",
 | 
					 | 
				
			||||||
            ".storybook/*",
 | 
					 | 
				
			||||||
            // don't lint build output (make sure it's set to your correct build folder name)
 | 
					 | 
				
			||||||
            // don't lint nyc coverage output
 | 
					 | 
				
			||||||
            "coverage/",
 | 
					 | 
				
			||||||
            "src/locale-codes.ts",
 | 
					 | 
				
			||||||
            "storybook-static/",
 | 
					 | 
				
			||||||
            "src/locales/",
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    eslint.configs.recommended,
 | 
					 | 
				
			||||||
    wcconf.configs["flat/recommended"],
 | 
					 | 
				
			||||||
    litconf.configs["flat/recommended"],
 | 
					 | 
				
			||||||
    ...tseslint.configs.recommended,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        languageOptions: {
 | 
					 | 
				
			||||||
            parser: tsparser,
 | 
					 | 
				
			||||||
            parserOptions: {
 | 
					 | 
				
			||||||
                ecmaVersion: 12,
 | 
					 | 
				
			||||||
                sourceType: "module",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        files: ["src/**"],
 | 
					 | 
				
			||||||
        rules: {
 | 
					 | 
				
			||||||
            "no-unused-vars": "off",
 | 
					 | 
				
			||||||
            "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
					 | 
				
			||||||
            "@typescript-eslint/ban-ts-comment": "off",
 | 
					 | 
				
			||||||
            "@typescript-eslint/no-unused-vars": [
 | 
					 | 
				
			||||||
                "error",
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    argsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                    varsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                    caughtErrorsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        languageOptions: {
 | 
					 | 
				
			||||||
            parser: tsparser,
 | 
					 | 
				
			||||||
            parserOptions: {
 | 
					 | 
				
			||||||
                ecmaVersion: 12,
 | 
					 | 
				
			||||||
                sourceType: "module",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            globals: {
 | 
					 | 
				
			||||||
                ...globals.nodeBuiltin,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        files: ["scripts/*.mjs", "*.ts", "*.mjs"],
 | 
					 | 
				
			||||||
        rules: {
 | 
					 | 
				
			||||||
            "no-unused-vars": "off",
 | 
					 | 
				
			||||||
            // We WANT our scripts to output to the console!
 | 
					 | 
				
			||||||
            "no-console": "off",
 | 
					 | 
				
			||||||
            "@typescript-eslint/ban-ts-comment": "off",
 | 
					 | 
				
			||||||
            "@typescript-eslint/no-unused-vars": [
 | 
					 | 
				
			||||||
                "error",
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    argsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                    varsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                    caughtErrorsIgnorePattern: "^_",
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
							
								
								
									
										10882
									
								
								tests/wdio/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10882
									
								
								tests/wdio/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,44 +1,37 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "name": "@goauthentik/web-tests",
 | 
					    "name": "@goauthentik/web-tests",
 | 
				
			||||||
    "dependencies": {
 | 
					    "private": true,
 | 
				
			||||||
        "chromedriver": "^129.0.1",
 | 
					    "type": "module",
 | 
				
			||||||
        "lockfile-lint": "^4.14.0",
 | 
					 | 
				
			||||||
        "syncpack": "^13.0.0"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@eslint/js": "^9.11.1",
 | 
					 | 
				
			||||||
        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
					        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
				
			||||||
        "@types/mocha": "^10.0.8",
 | 
					        "@typescript-eslint/eslint-plugin": "^7.17.0",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^8.7.0",
 | 
					        "@typescript-eslint/parser": "^7.17.0",
 | 
				
			||||||
        "@typescript-eslint/parser": "^8.7.0",
 | 
					        "@wdio/cli": "^9.0.3",
 | 
				
			||||||
        "@wdio/cli": "^9.1.2",
 | 
					        "@wdio/local-runner": "^9.0.1",
 | 
				
			||||||
        "@wdio/local-runner": "^9.1.2",
 | 
					        "@wdio/mocha-framework": "^8.40.2",
 | 
				
			||||||
        "@wdio/mocha-framework": "^9.1.2",
 | 
					        "@wdio/spec-reporter": "^8.39.0",
 | 
				
			||||||
        "@wdio/spec-reporter": "^9.1.2",
 | 
					        "eslint": "^8.57.0",
 | 
				
			||||||
        "eslint-plugin-lit": "^1.14.0",
 | 
					        "eslint-config-google": "^0.14.0",
 | 
				
			||||||
        "eslint-plugin-sonarjs": "^2.0.2",
 | 
					        "eslint-plugin-sonarjs": "^1.0.3",
 | 
				
			||||||
        "eslint-plugin-wc": "^2.1.0",
 | 
					 | 
				
			||||||
        "eslint": "^9.11.1",
 | 
					 | 
				
			||||||
        "npm-run-all": "^4.1.5",
 | 
					        "npm-run-all": "^4.1.5",
 | 
				
			||||||
        "prettier": "^3.3.3",
 | 
					        "prettier": "^3.3.3",
 | 
				
			||||||
        "typescript-eslint": "^8.7.0",
 | 
					        "ts-node": "^10.9.2",
 | 
				
			||||||
        "typescript": "^5.6.2",
 | 
					        "typescript": "^5.5.4",
 | 
				
			||||||
        "wdio-wait-for": "^3.0.11"
 | 
					        "wdio-wait-for": "^3.0.11"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "scripts": {
 | 
				
			||||||
 | 
					        "wdio": "wdio run ./wdio.conf.ts",
 | 
				
			||||||
 | 
					        "lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[AM?][M?]' | cut -d'/' -f3- | grep -E '\\.(ts|js|tsx|jsx)$')",
 | 
				
			||||||
 | 
					        "lint": "eslint . --max-warnings 0 --fix",
 | 
				
			||||||
 | 
					        "lint:spelling": "codespell -D - -D $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-dictionary.txt -I $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-words.txt ./test -s",
 | 
				
			||||||
 | 
					        "precommit": "run-s lint:precommit lint:spelling prettier",
 | 
				
			||||||
 | 
					        "prettier-check": "prettier --check .",
 | 
				
			||||||
 | 
					        "prettier": "prettier --write ."
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "engines": {
 | 
					    "engines": {
 | 
				
			||||||
        "node": ">=20"
 | 
					        "node": ">=20"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "private": true,
 | 
					    "dependencies": {
 | 
				
			||||||
    "scripts": {
 | 
					        "chromedriver": "^128.0.0"
 | 
				
			||||||
        "lint": "eslint . --max-warnings 0 --fix",
 | 
					    }
 | 
				
			||||||
        "lint:lockfile": "lockfile-lint --path package.json --type npm --allowed-hosts npm --validate-https",
 | 
					 | 
				
			||||||
        "lint:package": "syncpack format -i '    '",
 | 
					 | 
				
			||||||
        "lint:precommit": "eslint --max-warnings 0 --config ./.eslintrc.precommit.json $(git status --porcelain . | grep '^[AM?][M?]' | cut -d'/' -f3- | grep -E '\\.(ts|js|tsx|jsx)$')",
 | 
					 | 
				
			||||||
        "lint:spelling": "codespell -D - -D $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-dictionary.txt -I $(git rev-parse --show-toplevel 2> /dev/null)/.github/codespell-words.txt ./test -s",
 | 
					 | 
				
			||||||
        "precommit": "run-s lint:precommit lint:spelling prettier",
 | 
					 | 
				
			||||||
        "prettier": "prettier --write .",
 | 
					 | 
				
			||||||
        "prettier-check": "prettier --check .",
 | 
					 | 
				
			||||||
        "wdio": "wdio run ./wdio.conf.ts"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "type": "module"
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,25 @@
 | 
				
			|||||||
import Page from "../pageobjects/page.js";
 | 
					import Page from "../pageobjects/page.js";
 | 
				
			||||||
 | 
					import { browser } from "@wdio/globals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CLICK_TIME_DELAY = 250;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class AdminPage extends Page {
 | 
					export default class AdminPage extends Page {
 | 
				
			||||||
    public async pageHeader() {
 | 
					    public get pageHeader() {
 | 
				
			||||||
        return await $("ak-page-header").$('slot[name="header"]');
 | 
					        return $('>>>ak-page-header slot[name="header"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async openApplicationsListPage() {
 | 
					    async openApplicationsListPage() {
 | 
				
			||||||
        await this.open("if/admin/#/core/applications");
 | 
					        await this.open("if/admin/#/core/applications");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public open(path: string) {
 | 
				
			||||||
 | 
					        return browser.url(`http://localhost:9000/${path}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public pause(selector?: string) {
 | 
				
			||||||
 | 
					        if (selector) {
 | 
				
			||||||
 | 
					            return $(selector).waitForDisplayed();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return browser.pause(CLICK_TIME_DELAY);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,24 +27,24 @@ class ApplicationWizardView extends AdminPage {
 | 
				
			|||||||
    radius = RadiusForm;
 | 
					    radius = RadiusForm;
 | 
				
			||||||
    app = ApplicationForm;
 | 
					    app = ApplicationForm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async wizardTitle() {
 | 
					    get wizardTitle() {
 | 
				
			||||||
        return await $("ak-wizard-frame").$(".pf-c-wizard__title");
 | 
					        return $(">>>ak-wizard-frame .pf-c-wizard__header h1.pf-c-title");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async providerList() {
 | 
					    get providerList() {
 | 
				
			||||||
        return await $("ak-application-wizard-authentication-method-choice");
 | 
					        return $(">>>ak-application-wizard-authentication-method-choice");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async nextButton() {
 | 
					    get nextButton() {
 | 
				
			||||||
        return await $("ak-wizard-frame").$("footer button.pf-m-primary");
 | 
					        return $(">>>ak-wizard-frame footer button.pf-m-primary");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getProviderType(type: string) {
 | 
					    async getProviderType(type: string) {
 | 
				
			||||||
        return await this.providerList().$(`input[value="${type}"]`);
 | 
					        return await this.providerList.$(`>>>input[value="${type}"]`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async successMessage() {
 | 
					    get successMessage() {
 | 
				
			||||||
        return await $('[data-commit-state="success"]');
 | 
					        return $('>>>[data-commit-state="success"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -65,10 +65,8 @@ const providerValues: Pair[] = [
 | 
				
			|||||||
providerValues.forEach(([value, name]: Pair) => {
 | 
					providerValues.forEach(([value, name]: Pair) => {
 | 
				
			||||||
    Object.defineProperties(ApplicationWizardView.prototype, {
 | 
					    Object.defineProperties(ApplicationWizardView.prototype, {
 | 
				
			||||||
        [name]: {
 | 
					        [name]: {
 | 
				
			||||||
            get: async function () {
 | 
					            get: function () {
 | 
				
			||||||
                return await (
 | 
					                return this.providerList.$(`>>>input[value="${value}"]`);
 | 
				
			||||||
                    await this.providerList()
 | 
					 | 
				
			||||||
                ).$(`div[data-ouid-component-name="${value}"]`);
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
				
			|||||||
@ -9,8 +9,8 @@ class ApplicationsListPage extends AdminPage {
 | 
				
			|||||||
     * define selectors using getter methods
 | 
					     * define selectors using getter methods
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async startWizardButton() {
 | 
					    get startWizardButton() {
 | 
				
			||||||
        return await $("ak-application-wizard").$('button[slot="trigger"]');
 | 
					        return $('>>>ak-wizard-frame button[slot="trigger"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async open() {
 | 
					    async open() {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,16 +2,16 @@ import Page from "../page.js";
 | 
				
			|||||||
import { $ } from "@wdio/globals";
 | 
					import { $ } from "@wdio/globals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ApplicationForm extends Page {
 | 
					export class ApplicationForm extends Page {
 | 
				
			||||||
    async name() {
 | 
					    get name() {
 | 
				
			||||||
        return await $('ak-text-input[name="name"]').$("input");
 | 
					        return $('>>>ak-form-element-horizontal input[name="name"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async uiSettings() {
 | 
					    get uiSettings() {
 | 
				
			||||||
        return await $("ak-form-group").$('button[aria-label="UI Settings"]');
 | 
					        return $('>>>ak-form-group button[aria-label="UI Settings"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async launchUrl() {
 | 
					    get launchUrl() {
 | 
				
			||||||
        return await $('input[name="metaLaunchUrl"]');
 | 
					        return $('>>>input[name="metaLaunchUrl"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
 | 
				
			|||||||
export class ForwardProxyForm extends Page {
 | 
					export class ForwardProxyForm extends Page {
 | 
				
			||||||
    async setAuthorizationFlow(selector: string) {
 | 
					    async setAuthorizationFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-flow-search[name="authorizationFlow"]',
 | 
					            '>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            selector,
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get externalHost() {
 | 
					    get externalHost() {
 | 
				
			||||||
        return $('input[name="externalHost"]');
 | 
					        return $('>>>input[name="externalHost"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import Page from "../page.js";
 | 
					import Page from "../page.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class LdapForm extends Page {
 | 
					export class LdapForm extends Page {
 | 
				
			||||||
    async setBindFlow() {
 | 
					    async setBindFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-search-select-view[name="authorizationFlow"]',
 | 
					            '>>>ak-branded-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            "default-authentication-flow",
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
 | 
				
			|||||||
export class OauthForm extends Page {
 | 
					export class OauthForm extends Page {
 | 
				
			||||||
    async setAuthorizationFlow(selector: string) {
 | 
					    async setAuthorizationFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-flow-search[name="authorizationFlow"]',
 | 
					            '>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            `${selector}`,
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async providerName() {
 | 
					    get providerName() {
 | 
				
			||||||
        return await $('ak-form-element-horizontal[name="name"]').$("input");
 | 
					        return $('>>>ak-form-element-horizontal[name="name"] input');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,9 +3,9 @@ import Page from "../page.js";
 | 
				
			|||||||
export class RadiusForm extends Page {
 | 
					export class RadiusForm extends Page {
 | 
				
			||||||
    async setAuthenticationFlow(selector: string) {
 | 
					    async setAuthenticationFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-branded-flow-search[name="authorizationFlow"]',
 | 
					            '>>>ak-branded-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            selector,
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,14 @@ import { $ } from "@wdio/globals";
 | 
				
			|||||||
export class SamlForm extends Page {
 | 
					export class SamlForm extends Page {
 | 
				
			||||||
    async setAuthorizationFlow(selector: string) {
 | 
					    async setAuthorizationFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-flow-search[name="authorizationFlow"]',
 | 
					            '>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            selector,
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get acsUrl() {
 | 
					    get acsUrl() {
 | 
				
			||||||
        return $('input[name="acsUrl"]');
 | 
					        return $('>>>input[name="acsUrl"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,11 @@ import Page from "../page.js";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export class ScimForm extends Page {
 | 
					export class ScimForm extends Page {
 | 
				
			||||||
    get url() {
 | 
					    get url() {
 | 
				
			||||||
        return $('input[name="url"]');
 | 
					        return $('>>>input[name="url"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get token() {
 | 
					    get token() {
 | 
				
			||||||
        return $('input[name="token"]');
 | 
					        return $('>>>input[name="token"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,18 +4,18 @@ import { $ } from "@wdio/globals";
 | 
				
			|||||||
export class TransparentProxyForm extends Page {
 | 
					export class TransparentProxyForm extends Page {
 | 
				
			||||||
    async setAuthorizationFlow(selector: string) {
 | 
					    async setAuthorizationFlow(selector: string) {
 | 
				
			||||||
        await this.searchSelect(
 | 
					        await this.searchSelect(
 | 
				
			||||||
            'ak-flow-search[name="authorizationFlow"]',
 | 
					            '>>>ak-flow-search[name="authorizationFlow"] input[type="text"]',
 | 
				
			||||||
            "authorizationFlow",
 | 
					            "authorizationFlow",
 | 
				
			||||||
            selector,
 | 
					            `button*=${selector}`,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get externalHost() {
 | 
					    get externalHost() {
 | 
				
			||||||
        return $('input[name="externalHost"]');
 | 
					        return $('>>>input[name="externalHost"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get internalHost() {
 | 
					    get internalHost() {
 | 
				
			||||||
        return $('input[name="internalHost"]');
 | 
					        return $('>>>input[name="internalHost"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,20 +9,20 @@ class LoginPage extends Page {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Selectors
 | 
					     * Selectors
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async inputUsername() {
 | 
					    get inputUsername() {
 | 
				
			||||||
        return await $('input[name="uidField"]');
 | 
					        return $('>>>input[name="uidField"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async inputPassword() {
 | 
					    get inputPassword() {
 | 
				
			||||||
        return await $('input[name="password"]');
 | 
					        return $('>>>input[name="password"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async btnSubmit() {
 | 
					    get btnSubmit() {
 | 
				
			||||||
        return await $('button[type="submit"]');
 | 
					        return $('>>>button[type="submit"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async authFailure() {
 | 
					    get authFailure() {
 | 
				
			||||||
        return await $(".pf-m-error");
 | 
					        return $(">>>h4.pf-c-alert__title");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -30,15 +30,17 @@ class LoginPage extends Page {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async username(username: string) {
 | 
					    async username(username: string) {
 | 
				
			||||||
        await (await this.inputUsername()).setValue(username);
 | 
					        await this.inputUsername.waitForClickable();
 | 
				
			||||||
        await (await this.btnSubmit()).waitForEnabled();
 | 
					        await this.inputUsername.setValue(username);
 | 
				
			||||||
        await (await this.btnSubmit()).click();
 | 
					        await this.btnSubmit.waitForEnabled();
 | 
				
			||||||
 | 
					        await this.btnSubmit.click();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async password(password: string) {
 | 
					    async password(password: string) {
 | 
				
			||||||
        await (await this.inputPassword()).setValue(password);
 | 
					        await this.inputPassword.waitForClickable();
 | 
				
			||||||
        await (await this.btnSubmit()).waitForEnabled();
 | 
					        await this.inputPassword.setValue(password);
 | 
				
			||||||
        await (await this.btnSubmit()).click();
 | 
					        await this.btnSubmit.waitForEnabled();
 | 
				
			||||||
 | 
					        await this.btnSubmit.click();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async login(username: string, password: string) {
 | 
					    async login(username: string, password: string) {
 | 
				
			||||||
@ -46,7 +48,7 @@ class LoginPage extends Page {
 | 
				
			|||||||
        await this.pause();
 | 
					        await this.pause();
 | 
				
			||||||
        await this.password(password);
 | 
					        await this.password(password);
 | 
				
			||||||
        await this.pause();
 | 
					        await this.pause();
 | 
				
			||||||
        await this.pause("div.header h1");
 | 
					        await this.pause(">>>div.header h1");
 | 
				
			||||||
        return UserLibraryPage;
 | 
					        return UserLibraryPage;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import { browser } from "@wdio/globals";
 | 
					import { browser } from "@wdio/globals";
 | 
				
			||||||
import { Key } from "webdriverio";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const CLICK_TIME_DELAY = 250;
 | 
					const CLICK_TIME_DELAY = 250;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,15 +11,15 @@ export default class Page {
 | 
				
			|||||||
     * Opens a sub page of the page
 | 
					     * Opens a sub page of the page
 | 
				
			||||||
     * @param path path of the sub page (e.g. /path/to/page.html)
 | 
					     * @param path path of the sub page (e.g. /path/to/page.html)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public async open(path: string) {
 | 
					    public open(path: string) {
 | 
				
			||||||
        return await browser.url(`http://localhost:9000/${path}`);
 | 
					        return browser.url(`http://localhost:9000/${path}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async pause(selector?: string) {
 | 
					    public pause(selector?: string) {
 | 
				
			||||||
        if (selector) {
 | 
					        if (selector) {
 | 
				
			||||||
            return await $(selector).waitForDisplayed();
 | 
					            return $(selector).waitForDisplayed();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return await browser.pause(CLICK_TIME_DELAY);
 | 
					        return browser.pause(CLICK_TIME_DELAY);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -34,20 +33,10 @@ export default class Page {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async searchSelect(searchSelector: string, managedSelector: string, buttonSelector: string) {
 | 
					    async searchSelect(searchSelector: string, managedSelector: string, buttonSelector: string) {
 | 
				
			||||||
        const inputBind = await $(searchSelector);
 | 
					        const inputBind = await $(searchSelector);
 | 
				
			||||||
        const inputMain = await inputBind.$('input[type="text"]');
 | 
					        await inputBind.click();
 | 
				
			||||||
        await inputMain.click();
 | 
					        const searchBlock = await $(`>>>div[data-managed-for="${managedSelector}"]`);
 | 
				
			||||||
        const searchBlock = await (
 | 
					        const target = searchBlock.$(buttonSelector);
 | 
				
			||||||
            await $(`div[data-managed-for="${managedSelector}"]`).$("ak-list-select")
 | 
					        return await target.click();
 | 
				
			||||||
        ).shadow$$("button");
 | 
					 | 
				
			||||||
        let target: WebdriverIO.Element;
 | 
					 | 
				
			||||||
        for (const button of searchBlock) {
 | 
					 | 
				
			||||||
            if ((await button.getText()).includes(buttonSelector)) {
 | 
					 | 
				
			||||||
                target = button;
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        await (await target).click();
 | 
					 | 
				
			||||||
        await browser.keys(Key.Tab);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async logout() {
 | 
					    public async logout() {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,13 +9,13 @@ class UserLibraryPage extends Page {
 | 
				
			|||||||
     * define selectors using getter methods
 | 
					     * define selectors using getter methods
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async pageHeader() {
 | 
					    public get pageHeader() {
 | 
				
			||||||
        return await $('h1[aria-level="1"]');
 | 
					        return $('>>>h1[aria-level="1"]');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public async goToAdmin() {
 | 
					    public async goToAdmin() {
 | 
				
			||||||
        await $('a[href="/if/admin"]').click();
 | 
					        await $('>>>a[href="/if/admin"]').click();
 | 
				
			||||||
        return await $("ak-admin-overview").waitForDisplayed();
 | 
					        await $(">>>ak-admin-overview").waitForDisplayed();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +0,0 @@
 | 
				
			|||||||
import LoginPage from "../pageobjects/login.page.js";
 | 
					 | 
				
			||||||
import { BAD_PASSWORD, GOOD_USERNAME } from "../utils/constants.js";
 | 
					 | 
				
			||||||
import { expect } from "@wdio/globals";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe("Log into authentik", () => {
 | 
					 | 
				
			||||||
    it("should fail on a bad password", async () => {
 | 
					 | 
				
			||||||
        await LoginPage.open();
 | 
					 | 
				
			||||||
        await LoginPage.username(GOOD_USERNAME);
 | 
					 | 
				
			||||||
        await LoginPage.pause();
 | 
					 | 
				
			||||||
        await LoginPage.password(BAD_PASSWORD);
 | 
					 | 
				
			||||||
        const failure = await LoginPage.authFailure();
 | 
					 | 
				
			||||||
        await expect(failure).toBeDisplayedInViewport();
 | 
					 | 
				
			||||||
        await expect(failure).toHaveText("Invalid password");
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
@ -1,15 +1,21 @@
 | 
				
			|||||||
import LoginPage from "../pageobjects/login.page.js";
 | 
					import LoginPage from "../pageobjects/login.page.js";
 | 
				
			||||||
import { BAD_USERNAME, GOOD_PASSWORD } from "../utils/constants.js";
 | 
					import { BAD_PASSWORD, BAD_USERNAME, GOOD_USERNAME } from "../utils/constants.js";
 | 
				
			||||||
import { expect } from "@wdio/globals";
 | 
					import { expect } from "@wdio/globals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe("Log into authentik", () => {
 | 
					describe("Log into authentik", () => {
 | 
				
			||||||
    it("should fail on a bad username", async () => {
 | 
					    it("should fail on a bad username", async () => {
 | 
				
			||||||
        await LoginPage.open();
 | 
					        await LoginPage.open();
 | 
				
			||||||
        await LoginPage.username(BAD_USERNAME);
 | 
					        await LoginPage.username(BAD_USERNAME);
 | 
				
			||||||
 | 
					        const failure = await LoginPage.authFailure;
 | 
				
			||||||
 | 
					        expect(failure).toHaveText("Failed to authenticate.");
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("should fail on a bad password", async () => {
 | 
				
			||||||
 | 
					        await LoginPage.open();
 | 
				
			||||||
 | 
					        await LoginPage.username(GOOD_USERNAME);
 | 
				
			||||||
        await LoginPage.pause();
 | 
					        await LoginPage.pause();
 | 
				
			||||||
        await LoginPage.password(GOOD_PASSWORD);
 | 
					        await LoginPage.password(BAD_PASSWORD);
 | 
				
			||||||
        const failure = await LoginPage.authFailure();
 | 
					        const failure = await LoginPage.authFailure;
 | 
				
			||||||
        await expect(failure).toBeDisplayedInViewport();
 | 
					        expect(failure).toHaveText("Failed to authenticate.");
 | 
				
			||||||
        await expect(failure).toHaveText("Invalid password");
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -10,27 +10,25 @@ async function reachTheProvider(title: string) {
 | 
				
			|||||||
    await ApplicationsListPage.logout();
 | 
					    await ApplicationsListPage.logout();
 | 
				
			||||||
    await login();
 | 
					    await login();
 | 
				
			||||||
    await ApplicationsListPage.open();
 | 
					    await ApplicationsListPage.open();
 | 
				
			||||||
    await ApplicationsListPage.pause("ak-page-header");
 | 
					    await expect(await ApplicationsListPage.pageHeader).toHaveText("Applications");
 | 
				
			||||||
    await expect(await ApplicationsListPage.pageHeader()).toBeDisplayed();
 | 
					 | 
				
			||||||
    await expect(await ApplicationsListPage.pageHeader()).toHaveText("Applications");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await (await ApplicationsListPage.startWizardButton()).click();
 | 
					    await ApplicationsListPage.startWizardButton.click();
 | 
				
			||||||
    await (await ApplicationWizardView.wizardTitle()).waitForDisplayed();
 | 
					    await ApplicationWizardView.wizardTitle.waitForDisplayed();
 | 
				
			||||||
    await expect(await ApplicationWizardView.wizardTitle()).toHaveText("New application");
 | 
					    await expect(await ApplicationWizardView.wizardTitle).toHaveText("New application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await (await ApplicationWizardView.app.name()).setValue(`${title} - ${newPrefix}`);
 | 
					    await ApplicationWizardView.app.name.setValue(`${title} - ${newPrefix}`);
 | 
				
			||||||
    await (await ApplicationWizardView.app.uiSettings()).scrollIntoView();
 | 
					    await ApplicationWizardView.app.uiSettings.scrollIntoView();
 | 
				
			||||||
    await (await ApplicationWizardView.app.uiSettings()).click();
 | 
					    await ApplicationWizardView.app.uiSettings.click();
 | 
				
			||||||
    await (await ApplicationWizardView.app.launchUrl()).scrollIntoView();
 | 
					    await ApplicationWizardView.app.launchUrl.scrollIntoView();
 | 
				
			||||||
    await (await ApplicationWizardView.app.launchUrl()).setValue("http://example.goauthentik.io");
 | 
					    await ApplicationWizardView.app.launchUrl.setValue("http://example.goauthentik.io");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await (await ApplicationWizardView.nextButton()).click();
 | 
					    await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
    return await ApplicationWizardView.pause();
 | 
					    return await ApplicationWizardView.pause();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function getCommitMessage() {
 | 
					async function getCommitMessage() {
 | 
				
			||||||
    await (await ApplicationWizardView.successMessage()).waitForDisplayed();
 | 
					    await ApplicationWizardView.successMessage.waitForDisplayed();
 | 
				
			||||||
    return await ApplicationWizardView.successMessage();
 | 
					    return await ApplicationWizardView.successMessage;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SUCCESS_MESSAGE = "Your application has been saved";
 | 
					const SUCCESS_MESSAGE = "Your application has been saved";
 | 
				
			||||||
@ -40,97 +38,97 @@ describe("Configure Applications with the Application Wizard", () => {
 | 
				
			|||||||
    it("Should configure a simple LDAP Application", async () => {
 | 
					    it("Should configure a simple LDAP Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New LDAP Application");
 | 
					        await reachTheProvider("New LDAP Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.ldapProvider).scrollIntoView();
 | 
					        await ApplicationWizardView.ldapProvider.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.ldapProvider).click();
 | 
					        await ApplicationWizardView.ldapProvider.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.ldap.setBindFlow("default-authentication-flow");
 | 
					        await ApplicationWizardView.ldap.setBindFlow("default-authentication-flow");
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple Oauth2 Application", async () => {
 | 
					    it("Should configure a simple Oauth2 Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New Oauth2 Application");
 | 
					        await reachTheProvider("New Oauth2 Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.oauth2Provider).scrollIntoView();
 | 
					        await ApplicationWizardView.oauth2Provider.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.oauth2Provider).click();
 | 
					        await ApplicationWizardView.oauth2Provider.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.oauth.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
					        await ApplicationWizardView.oauth.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple SAML Application", async () => {
 | 
					    it("Should configure a simple SAML Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New SAML Application");
 | 
					        await reachTheProvider("New SAML Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.samlProvider).scrollIntoView();
 | 
					        await ApplicationWizardView.samlProvider.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.samlProvider).click();
 | 
					        await ApplicationWizardView.samlProvider.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.saml.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
					        await ApplicationWizardView.saml.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
				
			||||||
        await ApplicationWizardView.saml.acsUrl.setValue("http://example.com:8000/");
 | 
					        await ApplicationWizardView.saml.acsUrl.setValue("http://example.com:8000/");
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple SCIM Application", async () => {
 | 
					    it("Should configure a simple SCIM Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New SCIM Application");
 | 
					        await reachTheProvider("New SCIM Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.scimProvider).scrollIntoView();
 | 
					        await ApplicationWizardView.scimProvider.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.scimProvider).click();
 | 
					        await ApplicationWizardView.scimProvider.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.scim.url.setValue("http://example.com:8000/");
 | 
					        await ApplicationWizardView.scim.url.setValue("http://example.com:8000/");
 | 
				
			||||||
        await ApplicationWizardView.scim.token.setValue("a-very-basic-token");
 | 
					        await ApplicationWizardView.scim.token.setValue("a-very-basic-token");
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple Radius Application", async () => {
 | 
					    it("Should configure a simple Radius Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New Radius Application");
 | 
					        await reachTheProvider("New Radius Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.radiusProvider).scrollIntoView();
 | 
					        await ApplicationWizardView.radiusProvider.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.radiusProvider).click();
 | 
					        await ApplicationWizardView.radiusProvider.click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.radius.setAuthenticationFlow("default-authentication-flow");
 | 
					        await ApplicationWizardView.radius.setAuthenticationFlow("default-authentication-flow");
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple Transparent Proxy Application", async () => {
 | 
					    it("Should configure a simple Transparent Proxy Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New Transparent Proxy Application");
 | 
					        await reachTheProvider("New Transparent Proxy Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.proxyProviderProxy).scrollIntoView();
 | 
					        await ApplicationWizardView.proxyProviderProxy.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.proxyProviderProxy).click();
 | 
					        await ApplicationWizardView.proxyProviderProxy.click();
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.transparentProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
					        await ApplicationWizardView.transparentProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
				
			||||||
@ -141,19 +139,19 @@ describe("Configure Applications with the Application Wizard", () => {
 | 
				
			|||||||
            "http://internal.example.com",
 | 
					            "http://internal.example.com",
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it("Should configure a simple Forward Proxy Application", async () => {
 | 
					    it("Should configure a simple Forward Proxy Application", async () => {
 | 
				
			||||||
        await reachTheProvider("New Forward Proxy Application");
 | 
					        await reachTheProvider("New Forward Proxy Application");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.providerList()).waitForDisplayed();
 | 
					        await ApplicationWizardView.providerList.waitForDisplayed();
 | 
				
			||||||
        await (await ApplicationWizardView.proxyProviderForwardsingle).scrollIntoView();
 | 
					        await ApplicationWizardView.proxyProviderForwardsingle.scrollIntoView();
 | 
				
			||||||
        await (await ApplicationWizardView.proxyProviderForwardsingle).click();
 | 
					        await ApplicationWizardView.proxyProviderForwardsingle.click();
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await ApplicationWizardView.forwardProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
					        await ApplicationWizardView.forwardProxy.setAuthorizationFlow(EXPLICIT_CONSENT);
 | 
				
			||||||
@ -161,9 +159,9 @@ describe("Configure Applications with the Application Wizard", () => {
 | 
				
			|||||||
            "http://external.example.com",
 | 
					            "http://external.example.com",
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await (await ApplicationWizardView.nextButton()).click();
 | 
					        await ApplicationWizardView.nextButton.click();
 | 
				
			||||||
        await ApplicationWizardView.pause();
 | 
					        await ApplicationWizardView.pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await expect(await getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
					        await expect(getCommitMessage()).toHaveText(SUCCESS_MESSAGE);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -6,5 +6,5 @@ import { expect } from "@wdio/globals";
 | 
				
			|||||||
export const login = async () => {
 | 
					export const login = async () => {
 | 
				
			||||||
    await LoginPage.open();
 | 
					    await LoginPage.open();
 | 
				
			||||||
    await LoginPage.login(GOOD_USERNAME, GOOD_PASSWORD);
 | 
					    await LoginPage.login(GOOD_USERNAME, GOOD_PASSWORD);
 | 
				
			||||||
    await expect(await UserLibraryPage.pageHeader()).toHaveText("My applications");
 | 
					    await expect(UserLibraryPage.pageHeader).toHaveText("My applications");
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
        "moduleResolution": "node",
 | 
					        "moduleResolution": "node",
 | 
				
			||||||
        "module": "ESNext",
 | 
					        "module": "ESNext",
 | 
				
			||||||
        "target": "es2022",
 | 
					        "target": "es2022",
 | 
				
			||||||
        "types": ["node", "@wdio/globals/types", "expect-webdriverio", "@wdio/mocha-framework", "@types/mocha"],
 | 
					        "types": ["node", "@wdio/globals/types", "expect-webdriverio", "@wdio/mocha-framework"],
 | 
				
			||||||
        "skipLibCheck": true,
 | 
					        "skipLibCheck": true,
 | 
				
			||||||
        "noEmit": true,
 | 
					        "noEmit": true,
 | 
				
			||||||
        "allowImportingTsExtensions": true,
 | 
					        "allowImportingTsExtensions": true,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,19 @@
 | 
				
			|||||||
export const config: WebdriverIO.Config = {
 | 
					import type { Options } from "@wdio/types";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const config: Options.Testrunner = {
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    // ====================
 | 
					    // ====================
 | 
				
			||||||
    // Runner Configuration
 | 
					    // Runner Configuration
 | 
				
			||||||
    // ====================
 | 
					    // ====================
 | 
				
			||||||
    // WebdriverIO supports running e2e tests as well as unit and component tests.
 | 
					    // WebdriverIO supports running e2e tests as well as unit and component tests.
 | 
				
			||||||
    runner: "local",
 | 
					    runner: "local",
 | 
				
			||||||
    tsConfigPath: "./tsconfig.json",
 | 
					    autoCompileOpts: {
 | 
				
			||||||
 | 
					        autoCompile: true,
 | 
				
			||||||
 | 
					        tsNodeOpts: {
 | 
				
			||||||
 | 
					            project: "./tsconfig.json",
 | 
				
			||||||
 | 
					            transpileOnly: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    // ==================
 | 
					    // ==================
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								web/.eslintrc.precommit.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/.eslintrc.precommit.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "env": {
 | 
				
			||||||
 | 
					        "browser": true,
 | 
				
			||||||
 | 
					        "es2021": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "extends": [
 | 
				
			||||||
 | 
					        "eslint:recommended",
 | 
				
			||||||
 | 
					        "plugin:@typescript-eslint/recommended",
 | 
				
			||||||
 | 
					        "plugin:lit/recommended",
 | 
				
			||||||
 | 
					        "plugin:custom-elements/recommended",
 | 
				
			||||||
 | 
					        "plugin:storybook/recommended",
 | 
				
			||||||
 | 
					        "plugin:sonarjs/recommended"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "parser": "@typescript-eslint/parser",
 | 
				
			||||||
 | 
					    "parserOptions": {
 | 
				
			||||||
 | 
					        "ecmaVersion": 12,
 | 
				
			||||||
 | 
					        "sourceType": "module"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "plugins": ["@typescript-eslint", "lit", "custom-elements", "sonarjs"],
 | 
				
			||||||
 | 
					    "rules": {
 | 
				
			||||||
 | 
					        "indent": "off",
 | 
				
			||||||
 | 
					        "linebreak-style": ["error", "unix"],
 | 
				
			||||||
 | 
					        "quotes": ["error", "double", { "avoidEscape": true }],
 | 
				
			||||||
 | 
					        "semi": ["error", "always"],
 | 
				
			||||||
 | 
					        "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
 | 
					        "sonarjs/cognitive-complexity": ["warn", 9],
 | 
				
			||||||
 | 
					        "sonarjs/no-duplicate-string": "off",
 | 
				
			||||||
 | 
					        "sonarjs/no-nested-template-literals": "off"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9180
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9180
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -8,18 +8,18 @@
 | 
				
			|||||||
        "@codemirror/lang-xml": "^6.1.0",
 | 
					        "@codemirror/lang-xml": "^6.1.0",
 | 
				
			||||||
        "@codemirror/legacy-modes": "^6.4.1",
 | 
					        "@codemirror/legacy-modes": "^6.4.1",
 | 
				
			||||||
        "@codemirror/theme-one-dark": "^6.1.2",
 | 
					        "@codemirror/theme-one-dark": "^6.1.2",
 | 
				
			||||||
        "@floating-ui/dom": "^1.6.11",
 | 
					        "@floating-ui/dom": "^1.6.9",
 | 
				
			||||||
        "@formatjs/intl-listformat": "^7.5.7",
 | 
					        "@formatjs/intl-listformat": "^7.5.7",
 | 
				
			||||||
        "@fortawesome/fontawesome-free": "^6.6.0",
 | 
					        "@fortawesome/fontawesome-free": "^6.6.0",
 | 
				
			||||||
        "@goauthentik/api": "^2024.8.3-1727449099",
 | 
					        "@goauthentik/api": "^2024.6.3-1724414734",
 | 
				
			||||||
        "@lit/context": "^1.1.2",
 | 
					        "@lit/context": "^1.1.2",
 | 
				
			||||||
        "@lit/localize": "^0.12.2",
 | 
					        "@lit/localize": "^0.12.2",
 | 
				
			||||||
        "@lit/reactive-element": "^2.0.4",
 | 
					        "@lit/reactive-element": "^2.0.4",
 | 
				
			||||||
        "@lit/task": "^1.0.1",
 | 
					        "@lit/task": "^1.0.1",
 | 
				
			||||||
        "@open-wc/lit-helpers": "^0.7.0",
 | 
					        "@open-wc/lit-helpers": "^0.7.0",
 | 
				
			||||||
        "@patternfly/elements": "^4.0.2",
 | 
					        "@patternfly/elements": "^4.0.0",
 | 
				
			||||||
        "@patternfly/patternfly": "^4.224.2",
 | 
					        "@patternfly/patternfly": "^4.224.2",
 | 
				
			||||||
        "@sentry/browser": "^8.32.0",
 | 
					        "@sentry/browser": "^8.26.0",
 | 
				
			||||||
        "@webcomponents/webcomponentsjs": "^2.8.0",
 | 
					        "@webcomponents/webcomponentsjs": "^2.8.0",
 | 
				
			||||||
        "base64-js": "^1.5.1",
 | 
					        "base64-js": "^1.5.1",
 | 
				
			||||||
        "chart.js": "^4.4.4",
 | 
					        "chart.js": "^4.4.4",
 | 
				
			||||||
@ -32,13 +32,13 @@
 | 
				
			|||||||
        "guacamole-common-js": "^1.5.0",
 | 
					        "guacamole-common-js": "^1.5.0",
 | 
				
			||||||
        "lit": "^3.2.0",
 | 
					        "lit": "^3.2.0",
 | 
				
			||||||
        "md-front-matter": "^1.0.4",
 | 
					        "md-front-matter": "^1.0.4",
 | 
				
			||||||
        "mermaid": "^11.2.1",
 | 
					        "mermaid": "^11.0.2",
 | 
				
			||||||
        "rapidoc": "^9.3.7",
 | 
					        "rapidoc": "^9.3.4",
 | 
				
			||||||
        "showdown": "^2.1.0",
 | 
					        "showdown": "^2.1.0",
 | 
				
			||||||
        "style-mod": "^4.1.2",
 | 
					        "style-mod": "^4.1.2",
 | 
				
			||||||
        "ts-pattern": "^5.4.0",
 | 
					        "ts-pattern": "^5.3.1",
 | 
				
			||||||
        "webcomponent-qr-code": "^1.2.0",
 | 
					        "webcomponent-qr-code": "^1.2.0",
 | 
				
			||||||
        "yaml": "^2.5.1"
 | 
					        "yaml": "^2.5.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@babel/core": "^7.25.2",
 | 
					        "@babel/core": "^7.25.2",
 | 
				
			||||||
@ -49,48 +49,47 @@
 | 
				
			|||||||
        "@babel/plugin-transform-runtime": "^7.25.4",
 | 
					        "@babel/plugin-transform-runtime": "^7.25.4",
 | 
				
			||||||
        "@babel/preset-env": "^7.25.4",
 | 
					        "@babel/preset-env": "^7.25.4",
 | 
				
			||||||
        "@babel/preset-typescript": "^7.24.7",
 | 
					        "@babel/preset-typescript": "^7.24.7",
 | 
				
			||||||
        "@changesets/cli": "^2.27.8",
 | 
					        "@changesets/cli": "^2.27.5",
 | 
				
			||||||
        "@custom-elements-manifest/analyzer": "^0.10.2",
 | 
					        "@custom-elements-manifest/analyzer": "^0.10.2",
 | 
				
			||||||
        "@eslint/js": "^9.11.1",
 | 
					        "@eslint/js": "^9.9.1",
 | 
				
			||||||
        "@genesiscommunitysuccess/custom-elements-lsp": "^5.0.3",
 | 
					        "@genesiscommunitysuccess/custom-elements-lsp": "^5.0.3",
 | 
				
			||||||
        "@hcaptcha/types": "^1.0.4",
 | 
					        "@hcaptcha/types": "^1.0.4",
 | 
				
			||||||
        "@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
 | 
					        "@jeysal/storybook-addon-css-user-preferences": "^0.2.0",
 | 
				
			||||||
        "@lit/localize-tools": "^0.8.0",
 | 
					        "@lit/localize-tools": "^0.8.0",
 | 
				
			||||||
        "@rollup/plugin-replace": "^6.0.1",
 | 
					        "@rollup/plugin-replace": "^5.0.7",
 | 
				
			||||||
        "@spotlightjs/spotlight": "^2.4.1",
 | 
					        "@spotlightjs/spotlight": "^2.3.0",
 | 
				
			||||||
        "@storybook/addon-essentials": "^8.3.4",
 | 
					        "@storybook/addon-essentials": "^8.2.9",
 | 
				
			||||||
        "@storybook/addon-links": "^8.3.4",
 | 
					        "@storybook/addon-links": "^8.2.9",
 | 
				
			||||||
        "@storybook/api": "^7.6.17",
 | 
					        "@storybook/api": "^7.6.17",
 | 
				
			||||||
        "@storybook/blocks": "^8.0.8",
 | 
					        "@storybook/blocks": "^8.0.8",
 | 
				
			||||||
        "@storybook/manager-api": "^8.3.4",
 | 
					        "@storybook/manager-api": "^8.2.9",
 | 
				
			||||||
        "@storybook/web-components": "^8.3.4",
 | 
					        "@storybook/web-components": "^8.2.9",
 | 
				
			||||||
        "@storybook/web-components-vite": "^8.3.4",
 | 
					        "@storybook/web-components-vite": "^8.2.9",
 | 
				
			||||||
        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
					        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
				
			||||||
        "@types/chart.js": "^2.9.41",
 | 
					        "@types/chart.js": "^2.9.41",
 | 
				
			||||||
        "@types/codemirror": "5.60.15",
 | 
					        "@types/codemirror": "5.60.15",
 | 
				
			||||||
        "@types/eslint__js": "^8.42.3",
 | 
					        "@types/eslint__js": "^8.42.3",
 | 
				
			||||||
        "@types/grecaptcha": "^3.0.9",
 | 
					        "@types/grecaptcha": "^3.0.9",
 | 
				
			||||||
        "@types/guacamole-common-js": "1.5.2",
 | 
					        "@types/guacamole-common-js": "1.5.2",
 | 
				
			||||||
        "@types/node": "^22.7.4",
 | 
					 | 
				
			||||||
        "@types/showdown": "^2.0.6",
 | 
					        "@types/showdown": "^2.0.6",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^8.7.0",
 | 
					        "@typescript-eslint/eslint-plugin": "^8.0.1",
 | 
				
			||||||
        "@typescript-eslint/parser": "^8.7.0",
 | 
					        "@typescript-eslint/parser": "^8.0.1",
 | 
				
			||||||
        "@wdio/browser-runner": "^8.40.5",
 | 
					        "@wdio/browser-runner": "^8.40.2",
 | 
				
			||||||
        "@wdio/cli": "^8.40.5",
 | 
					        "@wdio/cli": "^8.40.2",
 | 
				
			||||||
        "@wdio/mocha-framework": "^9.1.0",
 | 
					        "@wdio/mocha-framework": "^8.40.2",
 | 
				
			||||||
        "@wdio/spec-reporter": "^9.1.0",
 | 
					        "@wdio/spec-reporter": "^8.36.1",
 | 
				
			||||||
        "babel-plugin-macros": "^3.1.0",
 | 
					        "babel-plugin-macros": "^3.1.0",
 | 
				
			||||||
        "babel-plugin-tsconfig-paths": "^1.0.3",
 | 
					        "babel-plugin-tsconfig-paths": "^1.0.3",
 | 
				
			||||||
        "chokidar": "^4.0.1",
 | 
					        "chokidar": "^3.6.0",
 | 
				
			||||||
        "cross-env": "^7.0.3",
 | 
					        "cross-env": "^7.0.3",
 | 
				
			||||||
        "esbuild": "^0.24.0",
 | 
					        "esbuild": "^0.23.1",
 | 
				
			||||||
        "eslint": "^9.11.1",
 | 
					        "eslint": "^9.8.0",
 | 
				
			||||||
        "eslint-plugin-lit": "^1.14.0",
 | 
					        "eslint-plugin-lit": "^1.14.0",
 | 
				
			||||||
 | 
					        "eslint-plugin-sonarjs": "^1.0.4",
 | 
				
			||||||
        "eslint-plugin-wc": "^2.1.0",
 | 
					        "eslint-plugin-wc": "^2.1.0",
 | 
				
			||||||
        "github-slugger": "^2.0.0",
 | 
					        "github-slugger": "^2.0.0",
 | 
				
			||||||
        "glob": "^11.0.0",
 | 
					        "glob": "^11.0.0",
 | 
				
			||||||
        "globals": "^15.9.0",
 | 
					        "globals": "^15.9.0",
 | 
				
			||||||
        "knip": "^5.30.6",
 | 
					 | 
				
			||||||
        "lit-analyzer": "^2.0.3",
 | 
					        "lit-analyzer": "^2.0.3",
 | 
				
			||||||
        "npm-run-all": "^4.1.5",
 | 
					        "npm-run-all": "^4.1.5",
 | 
				
			||||||
        "prettier": "^3.3.3",
 | 
					        "prettier": "^3.3.3",
 | 
				
			||||||
@ -105,24 +104,24 @@
 | 
				
			|||||||
        "ts-lit-plugin": "^2.0.2",
 | 
					        "ts-lit-plugin": "^2.0.2",
 | 
				
			||||||
        "ts-node": "^10.9.2",
 | 
					        "ts-node": "^10.9.2",
 | 
				
			||||||
        "tslib": "^2.7.0",
 | 
					        "tslib": "^2.7.0",
 | 
				
			||||||
        "turnstile-types": "^1.2.3",
 | 
					        "turnstile-types": "^1.2.2",
 | 
				
			||||||
        "typescript": "^5.6.2",
 | 
					        "typescript": "^5.5.4",
 | 
				
			||||||
        "typescript-eslint": "^8.7.0",
 | 
					        "typescript-eslint": "^8.2.0",
 | 
				
			||||||
        "vite-tsconfig-paths": "^5.0.1",
 | 
					        "vite-tsconfig-paths": "^5.0.1",
 | 
				
			||||||
        "wdio-wait-for": "^3.0.11",
 | 
					        "wdio-wait-for": "^3.0.11",
 | 
				
			||||||
        "wireit": "^0.14.9"
 | 
					        "wireit": "^0.14.8"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "engines": {
 | 
					    "engines": {
 | 
				
			||||||
        "node": ">=20"
 | 
					        "node": ">=20"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "license": "MIT",
 | 
					    "license": "MIT",
 | 
				
			||||||
    "optionalDependencies": {
 | 
					    "optionalDependencies": {
 | 
				
			||||||
        "@esbuild/darwin-arm64": "^0.24.0",
 | 
					        "@esbuild/darwin-arm64": "^0.23.0",
 | 
				
			||||||
        "@esbuild/linux-amd64": "^0.18.11",
 | 
					        "@esbuild/linux-amd64": "^0.18.11",
 | 
				
			||||||
        "@esbuild/linux-arm64": "^0.24.0",
 | 
					        "@esbuild/linux-arm64": "^0.23.0",
 | 
				
			||||||
        "@rollup/rollup-darwin-arm64": "4.22.5",
 | 
					        "@rollup/rollup-darwin-arm64": "4.21.0",
 | 
				
			||||||
        "@rollup/rollup-linux-arm64-gnu": "4.22.5",
 | 
					        "@rollup/rollup-linux-arm64-gnu": "4.21.0",
 | 
				
			||||||
        "@rollup/rollup-linux-x64-gnu": "4.22.5"
 | 
					        "@rollup/rollup-linux-x64-gnu": "4.21.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "private": true,
 | 
					    "private": true,
 | 
				
			||||||
    "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
@ -135,7 +134,6 @@
 | 
				
			|||||||
        "extract-locales": "wireit",
 | 
					        "extract-locales": "wireit",
 | 
				
			||||||
        "format": "wireit",
 | 
					        "format": "wireit",
 | 
				
			||||||
        "lint": "wireit",
 | 
					        "lint": "wireit",
 | 
				
			||||||
        "lint:imports": "wireit",
 | 
					 | 
				
			||||||
        "lint:lockfile": "wireit",
 | 
					        "lint:lockfile": "wireit",
 | 
				
			||||||
        "lint:nightmare": "wireit",
 | 
					        "lint:nightmare": "wireit",
 | 
				
			||||||
        "lint:package": "wireit",
 | 
					        "lint:package": "wireit",
 | 
				
			||||||
@ -150,7 +148,8 @@
 | 
				
			|||||||
        "storybook:build": "wireit",
 | 
					        "storybook:build": "wireit",
 | 
				
			||||||
        "storybook:build-import-map": "wireit",
 | 
					        "storybook:build-import-map": "wireit",
 | 
				
			||||||
        "test": "wireit",
 | 
					        "test": "wireit",
 | 
				
			||||||
        "test-watch": "wireit",
 | 
					        "test-view": "wireit",
 | 
				
			||||||
 | 
					        "test-watch": "npx wdio run ./wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json --watch",
 | 
				
			||||||
        "tsc": "wireit",
 | 
					        "tsc": "wireit",
 | 
				
			||||||
        "watch": "run-s build-locales esbuild:watch"
 | 
					        "watch": "run-s build-locales esbuild:watch"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -250,9 +249,6 @@
 | 
				
			|||||||
        "lint:components": {
 | 
					        "lint:components": {
 | 
				
			||||||
            "command": "lit-analyzer src"
 | 
					            "command": "lit-analyzer src"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "lint:imports": {
 | 
					 | 
				
			||||||
            "command": "knip --config scripts/knip.config.ts"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "lint:types": {
 | 
					        "lint:types": {
 | 
				
			||||||
            "command": "tsc --noEmit -p .",
 | 
					            "command": "tsc --noEmit -p .",
 | 
				
			||||||
            "dependencies": [
 | 
					            "dependencies": [
 | 
				
			||||||
@ -262,7 +258,7 @@
 | 
				
			|||||||
        "lint:lockfile": {
 | 
					        "lint:lockfile": {
 | 
				
			||||||
            "__comment": "The lockfile-lint package does not have an option to ensure resolved hashes are set everywhere",
 | 
					            "__comment": "The lockfile-lint package does not have an option to ensure resolved hashes are set everywhere",
 | 
				
			||||||
            "shell": true,
 | 
					            "shell": true,
 | 
				
			||||||
            "command": "[ -z \"$(jq -r '.packages | to_entries[] | select((.key | contains(\"node_modules\")) and (.value | has(\"resolved\") | not)) | .key' < package-lock.json)\" ]"
 | 
					            "command": "[ -z \"$(jq -r '.packages | to_entries[] | select((.key | startswith(\"node_modules\")) and (.value | has(\"resolved\") | not)) | .key' < package-lock.json)\" ]"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "lint:lockfiles": {
 | 
					        "lint:lockfiles": {
 | 
				
			||||||
            "dependencies": [
 | 
					            "dependencies": [
 | 
				
			||||||
@ -329,14 +325,14 @@
 | 
				
			|||||||
            "command": "node scripts/build-storybook-import-maps.mjs"
 | 
					            "command": "node scripts/build-storybook-import-maps.mjs"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "test": {
 | 
					        "test": {
 | 
				
			||||||
            "command": "wdio ./wdio.conf.ts --logLevel=info",
 | 
					            "command": "wdio run ./wdio.conf.ts --logLevel=warn",
 | 
				
			||||||
            "env": {
 | 
					            "env": {
 | 
				
			||||||
                "CI": "true",
 | 
					                "CI": "true",
 | 
				
			||||||
                "TS_NODE_PROJECT": "tsconfig.test.json"
 | 
					                "TS_NODE_PROJECT": "tsconfig.test.json"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "test-watch": {
 | 
					        "test-view": {
 | 
				
			||||||
            "command": "wdio ./wdio.conf.ts",
 | 
					            "command": "wdio run ./wdio.conf.ts",
 | 
				
			||||||
            "env": {
 | 
					            "env": {
 | 
				
			||||||
                "TS_NODE_PROJECT": "tsconfig.test.json"
 | 
					                "TS_NODE_PROJECT": "tsconfig.test.json"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,22 +10,22 @@
 | 
				
			|||||||
        "weakmap-polyfill": "^2.0.4"
 | 
					        "weakmap-polyfill": "^2.0.4"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@rollup/plugin-commonjs": "^28.0.0",
 | 
					        "@rollup/plugin-commonjs": "^26.0.1",
 | 
				
			||||||
        "@rollup/plugin-node-resolve": "^15.3.0",
 | 
					        "@rollup/plugin-node-resolve": "^15.2.3",
 | 
				
			||||||
        "@rollup/plugin-swc": "^0.4.0",
 | 
					        "@rollup/plugin-swc": "^0.3.1",
 | 
				
			||||||
        "@swc/cli": "^0.4.0",
 | 
					        "@swc/cli": "^0.4.0",
 | 
				
			||||||
        "@swc/core": "^1.7.28",
 | 
					        "@swc/core": "^1.7.18",
 | 
				
			||||||
        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
					        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
				
			||||||
        "@types/jquery": "^3.5.31",
 | 
					        "@types/jquery": "^3.5.30",
 | 
				
			||||||
        "lockfile-lint": "^4.14.0",
 | 
					        "lockfile-lint": "^4.14.0",
 | 
				
			||||||
        "prettier": "^3.3.2",
 | 
					        "prettier": "^3.3.2",
 | 
				
			||||||
        "rollup": "^4.22.5",
 | 
					        "rollup": "^4.21.0",
 | 
				
			||||||
        "rollup-plugin-copy": "^3.5.0",
 | 
					        "rollup-plugin-copy": "^3.5.0",
 | 
				
			||||||
        "wireit": "^0.14.9"
 | 
					        "wireit": "^0.14.8"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "license": "MIT",
 | 
					    "license": "MIT",
 | 
				
			||||||
    "optionalDependencies": {
 | 
					    "optionalDependencies": {
 | 
				
			||||||
        "@swc/core": "^1.7.28",
 | 
					        "@swc/core": "^1.7.18",
 | 
				
			||||||
        "@swc/core-darwin-arm64": "^1.6.13",
 | 
					        "@swc/core-darwin-arm64": "^1.6.13",
 | 
				
			||||||
        "@swc/core-darwin-x64": "^1.6.13",
 | 
					        "@swc/core-darwin-x64": "^1.6.13",
 | 
				
			||||||
        "@swc/core-linux-arm-gnueabihf": "^1.6.13",
 | 
					        "@swc/core-linux-arm-gnueabihf": "^1.6.13",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										67
									
								
								web/scripts/eslint-nightmare.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								web/scripts/eslint-nightmare.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					import { execFileSync } from "child_process";
 | 
				
			||||||
 | 
					import { ESLint } from "eslint";
 | 
				
			||||||
 | 
					import path from "path";
 | 
				
			||||||
 | 
					import process from "process";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Code assumes this script is in the './web/scripts' folder.
 | 
				
			||||||
 | 
					const projectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
 | 
				
			||||||
 | 
					    encoding: "utf8",
 | 
				
			||||||
 | 
					}).replace("\n", "");
 | 
				
			||||||
 | 
					process.chdir(path.join(projectRoot, "./web"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eslintConfig = {
 | 
				
			||||||
 | 
					    fix: true,
 | 
				
			||||||
 | 
					    overrideConfig: {
 | 
				
			||||||
 | 
					        env: {
 | 
				
			||||||
 | 
					            browser: true,
 | 
				
			||||||
 | 
					            es2021: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        extends: [
 | 
				
			||||||
 | 
					            "eslint:recommended",
 | 
				
			||||||
 | 
					            "plugin:@typescript-eslint/recommended",
 | 
				
			||||||
 | 
					            "plugin:lit/recommended",
 | 
				
			||||||
 | 
					            "plugin:custom-elements/recommended",
 | 
				
			||||||
 | 
					            "plugin:storybook/recommended",
 | 
				
			||||||
 | 
					            "plugin:sonarjs/recommended",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        parser: "@typescript-eslint/parser",
 | 
				
			||||||
 | 
					        parserOptions: {
 | 
				
			||||||
 | 
					            ecmaVersion: 12,
 | 
				
			||||||
 | 
					            sourceType: "module",
 | 
				
			||||||
 | 
					            project: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        plugins: ["@typescript-eslint", "lit", "custom-elements", "sonarjs"],
 | 
				
			||||||
 | 
					        ignorePatterns: ["authentik-live-tests/**", "./.storybook/**/*.ts"],
 | 
				
			||||||
 | 
					        rules: {
 | 
				
			||||||
 | 
					            "indent": "off",
 | 
				
			||||||
 | 
					            "linebreak-style": ["error", "unix"],
 | 
				
			||||||
 | 
					            "quotes": ["error", "double", { avoidEscape: true }],
 | 
				
			||||||
 | 
					            "semi": ["error", "always"],
 | 
				
			||||||
 | 
					            "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
 | 
					            "no-unused-vars": "off",
 | 
				
			||||||
 | 
					            "sonarjs/cognitive-complexity": ["warn", 9],
 | 
				
			||||||
 | 
					            "sonarjs/no-duplicate-string": "off",
 | 
				
			||||||
 | 
					            "sonarjs/no-nested-template-literals": "off",
 | 
				
			||||||
 | 
					            "@typescript-eslint/no-unused-vars": [
 | 
				
			||||||
 | 
					                "error",
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    argsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                    varsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                    caughtErrorsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const updated = ["./src/", "./build.mjs", "./scripts/*.mjs"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eslint = new ESLint(eslintConfig);
 | 
				
			||||||
 | 
					const results = await eslint.lintFiles(updated);
 | 
				
			||||||
 | 
					const formatter = await eslint.loadFormatter("stylish");
 | 
				
			||||||
 | 
					const resultText = formatter.format(results);
 | 
				
			||||||
 | 
					const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(resultText);
 | 
				
			||||||
 | 
					process.exit(errors > 1 ? 1 : 0);
 | 
				
			||||||
							
								
								
									
										94
									
								
								web/scripts/eslint-precommit.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								web/scripts/eslint-precommit.mjs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					import { execFileSync } from "child_process";
 | 
				
			||||||
 | 
					import { ESLint } from "eslint";
 | 
				
			||||||
 | 
					import path from "path";
 | 
				
			||||||
 | 
					import process from "process";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Code assumes this script is in the './web/scripts' folder.
 | 
				
			||||||
 | 
					const projectRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
 | 
				
			||||||
 | 
					    encoding: "utf8",
 | 
				
			||||||
 | 
					}).replace("\n", "");
 | 
				
			||||||
 | 
					process.chdir(path.join(projectRoot, "./web"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eslintConfig = {
 | 
				
			||||||
 | 
					    fix: true,
 | 
				
			||||||
 | 
					    overrideConfig: {
 | 
				
			||||||
 | 
					        env: {
 | 
				
			||||||
 | 
					            browser: true,
 | 
				
			||||||
 | 
					            es2021: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        extends: [
 | 
				
			||||||
 | 
					            "eslint:recommended",
 | 
				
			||||||
 | 
					            "plugin:@typescript-eslint/recommended",
 | 
				
			||||||
 | 
					            "plugin:lit/recommended",
 | 
				
			||||||
 | 
					            "plugin:custom-elements/recommended",
 | 
				
			||||||
 | 
					            "plugin:storybook/recommended",
 | 
				
			||||||
 | 
					            "plugin:sonarjs/recommended",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        parser: "@typescript-eslint/parser",
 | 
				
			||||||
 | 
					        parserOptions: {
 | 
				
			||||||
 | 
					            ecmaVersion: 12,
 | 
				
			||||||
 | 
					            sourceType: "module",
 | 
				
			||||||
 | 
					            project: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        plugins: ["@typescript-eslint", "lit", "custom-elements", "sonarjs"],
 | 
				
			||||||
 | 
					        ignorePatterns: ["authentik-live-tests/**", "./.storybook/**/*.ts"],
 | 
				
			||||||
 | 
					        rules: {
 | 
				
			||||||
 | 
					            "indent": "off",
 | 
				
			||||||
 | 
					            "linebreak-style": ["error", "unix"],
 | 
				
			||||||
 | 
					            "quotes": ["error", "double", { avoidEscape: true }],
 | 
				
			||||||
 | 
					            "semi": ["error", "always"],
 | 
				
			||||||
 | 
					            "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
 | 
					            "no-unused-vars": "off",
 | 
				
			||||||
 | 
					            "sonarjs/cognitive-complexity": ["warn", 9],
 | 
				
			||||||
 | 
					            "sonarjs/no-duplicate-string": "off",
 | 
				
			||||||
 | 
					            "sonarjs/no-nested-template-literals": "off",
 | 
				
			||||||
 | 
					            "@typescript-eslint/no-unused-vars": [
 | 
				
			||||||
 | 
					                "error",
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    argsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                    varsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                    caughtErrorsIgnorePattern: "^_",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const porcelainV1 = /^(..)\s+(.*$)/;
 | 
				
			||||||
 | 
					const gitStatus = execFileSync("git", ["status", "--porcelain", "."], { encoding: "utf8" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const statuses = gitStatus.split("\n").reduce((acc, line) => {
 | 
				
			||||||
 | 
					    const match = porcelainV1.exec(line.replace("\n"));
 | 
				
			||||||
 | 
					    if (!match) {
 | 
				
			||||||
 | 
					        return acc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const [status, path] = Array.from(match).slice(1, 3);
 | 
				
			||||||
 | 
					    return [...acc, [status, path.split("\x00")[0]]];
 | 
				
			||||||
 | 
					}, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isModified = /^(M|\?|\s)(M|\?|\s)/;
 | 
				
			||||||
 | 
					const modified = (s) => isModified.test(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isCheckable = /\.(ts|js|mjs)$/;
 | 
				
			||||||
 | 
					const checkable = (s) => isCheckable.test(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ignored = /\/\.storybook\//;
 | 
				
			||||||
 | 
					const notIgnored = (s) => !ignored.test(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const updated = statuses.reduce(
 | 
				
			||||||
 | 
					    (acc, [status, filename]) =>
 | 
				
			||||||
 | 
					        modified(status) && checkable(filename) && notIgnored(filename)
 | 
				
			||||||
 | 
					            ? [...acc, path.join(projectRoot, filename)]
 | 
				
			||||||
 | 
					            : acc,
 | 
				
			||||||
 | 
					    [],
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const eslint = new ESLint(eslintConfig);
 | 
				
			||||||
 | 
					const results = await eslint.lintFiles(updated);
 | 
				
			||||||
 | 
					const formatter = await eslint.loadFormatter("stylish");
 | 
				
			||||||
 | 
					const resultText = formatter.format(results);
 | 
				
			||||||
 | 
					const errors = results.reduce((acc, result) => acc + result.errorCount, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(resultText);
 | 
				
			||||||
 | 
					process.exit(errors > 1 ? 1 : 0);
 | 
				
			||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import eslint from "@eslint/js";
 | 
					import eslint from "@eslint/js";
 | 
				
			||||||
import tsparser from "@typescript-eslint/parser";
 | 
					import tsparser from "@typescript-eslint/parser";
 | 
				
			||||||
import litconf from "eslint-plugin-lit";
 | 
					import litconf from "eslint-plugin-lit";
 | 
				
			||||||
 | 
					import sonar from "eslint-plugin-sonarjs";
 | 
				
			||||||
import wcconf from "eslint-plugin-wc";
 | 
					import wcconf from "eslint-plugin-wc";
 | 
				
			||||||
import globals from "globals";
 | 
					import globals from "globals";
 | 
				
			||||||
import tseslint from "typescript-eslint";
 | 
					import tseslint from "typescript-eslint";
 | 
				
			||||||
@ -8,9 +9,7 @@ import tseslint from "typescript-eslint";
 | 
				
			|||||||
const MAX_DEPTH = 4;
 | 
					const MAX_DEPTH = 4;
 | 
				
			||||||
const MAX_NESTED_CALLBACKS = 4;
 | 
					const MAX_NESTED_CALLBACKS = 4;
 | 
				
			||||||
const MAX_PARAMS = 5;
 | 
					const MAX_PARAMS = 5;
 | 
				
			||||||
 | 
					const MAX_COGNITIVE_COMPLEXITY = 9;
 | 
				
			||||||
// Waiting for SonarJS to be compatible
 | 
					 | 
				
			||||||
// const MAX_COGNITIVE_COMPLEXITY = 9;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rules = {
 | 
					const rules = {
 | 
				
			||||||
    "accessor-pairs": "error",
 | 
					    "accessor-pairs": "error",
 | 
				
			||||||
@ -129,11 +128,9 @@ const rules = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    "no-unused-vars": "off",
 | 
					    "no-unused-vars": "off",
 | 
				
			||||||
    "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
					    "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
				
			||||||
    // SonarJS is not yet compatible with ESLint 9.  Commenting these out
 | 
					    "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
 | 
				
			||||||
    // until it is.
 | 
					    "sonarjs/no-duplicate-string": "off",
 | 
				
			||||||
    //    "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
 | 
					    "sonarjs/no-nested-template-literals": "off",
 | 
				
			||||||
    //    "sonarjs/no-duplicate-string": "off",
 | 
					 | 
				
			||||||
    //    "sonarjs/no-nested-template-literals": "off",
 | 
					 | 
				
			||||||
    "@typescript-eslint/ban-ts-comment": "off",
 | 
					    "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
    "@typescript-eslint/no-unused-vars": [
 | 
					    "@typescript-eslint/no-unused-vars": [
 | 
				
			||||||
        "error",
 | 
					        "error",
 | 
				
			||||||
@ -170,7 +167,7 @@ export default [
 | 
				
			|||||||
    wcconf.configs["flat/recommended"],
 | 
					    wcconf.configs["flat/recommended"],
 | 
				
			||||||
    litconf.configs["flat/recommended"],
 | 
					    litconf.configs["flat/recommended"],
 | 
				
			||||||
    ...tseslint.configs.recommended,
 | 
					    ...tseslint.configs.recommended,
 | 
				
			||||||
    //     sonar.configs.recommended,
 | 
					    sonar.configs.recommended,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        languageOptions: {
 | 
					        languageOptions: {
 | 
				
			||||||
            parser: tsparser,
 | 
					            parser: tsparser,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import eslint from "@eslint/js";
 | 
					import eslint from "@eslint/js";
 | 
				
			||||||
import tsparser from "@typescript-eslint/parser";
 | 
					import tsparser from "@typescript-eslint/parser";
 | 
				
			||||||
import litconf from "eslint-plugin-lit";
 | 
					import litconf from "eslint-plugin-lit";
 | 
				
			||||||
 | 
					import sonar from "eslint-plugin-sonarjs";
 | 
				
			||||||
import wcconf from "eslint-plugin-wc";
 | 
					import wcconf from "eslint-plugin-wc";
 | 
				
			||||||
import globals from "globals";
 | 
					import globals from "globals";
 | 
				
			||||||
import tseslint from "typescript-eslint";
 | 
					import tseslint from "typescript-eslint";
 | 
				
			||||||
@ -29,7 +30,7 @@ export default [
 | 
				
			|||||||
    wcconf.configs["flat/recommended"],
 | 
					    wcconf.configs["flat/recommended"],
 | 
				
			||||||
    litconf.configs["flat/recommended"],
 | 
					    litconf.configs["flat/recommended"],
 | 
				
			||||||
    ...tseslint.configs.recommended,
 | 
					    ...tseslint.configs.recommended,
 | 
				
			||||||
    //    sonar.configs.recommended,
 | 
					    sonar.configs.recommended,
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        languageOptions: {
 | 
					        languageOptions: {
 | 
				
			||||||
            parser: tsparser,
 | 
					            parser: tsparser,
 | 
				
			||||||
@ -42,11 +43,9 @@ export default [
 | 
				
			|||||||
        rules: {
 | 
					        rules: {
 | 
				
			||||||
            "no-unused-vars": "off",
 | 
					            "no-unused-vars": "off",
 | 
				
			||||||
            "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
					            "no-console": ["error", { allow: ["debug", "warn", "error"] }],
 | 
				
			||||||
            // SonarJS is not yet compatible with ESLint 9.  Commenting these out
 | 
					            "sonarjs/cognitive-complexity": ["off", 9],
 | 
				
			||||||
            // until it is.
 | 
					            "sonarjs/no-duplicate-string": "off",
 | 
				
			||||||
            //    "sonarjs/cognitive-complexity": ["off", 9],
 | 
					            "sonarjs/no-nested-template-literals": "off",
 | 
				
			||||||
            //    "sonarjs/no-duplicate-string": "off",
 | 
					 | 
				
			||||||
            //    "sonarjs/no-nested-template-literals": "off",
 | 
					 | 
				
			||||||
            "@typescript-eslint/ban-ts-comment": "off",
 | 
					            "@typescript-eslint/ban-ts-comment": "off",
 | 
				
			||||||
            "@typescript-eslint/no-unused-vars": [
 | 
					            "@typescript-eslint/no-unused-vars": [
 | 
				
			||||||
                "error",
 | 
					                "error",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
import { type KnipConfig } from "knip";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const config: KnipConfig = {
 | 
					 | 
				
			||||||
    "entry": [
 | 
					 | 
				
			||||||
        "./src/admin/AdminInterface/AdminInterface.ts",
 | 
					 | 
				
			||||||
        "./src/user/UserInterface.ts",
 | 
					 | 
				
			||||||
        "./src/flow/FlowInterface.ts",
 | 
					 | 
				
			||||||
        "./src/standalone/api-browser/index.ts",
 | 
					 | 
				
			||||||
        "./src/enterprise/rac/index.ts",
 | 
					 | 
				
			||||||
        "./src/standalone/loading/index.ts",
 | 
					 | 
				
			||||||
        "./src/polyfill/poly.ts",
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "project": ["src/**/*.ts", "src/**/*.js", "./scripts/*.mjs", ".storybook/*.ts"],
 | 
					 | 
				
			||||||
    // "ignore": ["src/**/*.test.ts", "src/**/*.stories.ts"],
 | 
					 | 
				
			||||||
    // Prevent Knip from complaining about web components, which export their classes but also
 | 
					 | 
				
			||||||
    // export their registration, and we don't always use both.
 | 
					 | 
				
			||||||
    "ignoreExportsUsedInFile": true,
 | 
					 | 
				
			||||||
    "typescript": {
 | 
					 | 
				
			||||||
        config: ["tsconfig.json"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "wireit": {
 | 
					 | 
				
			||||||
        config: ["package.json"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "storybook": {
 | 
					 | 
				
			||||||
        config: [".storybook/{main,test-runner}.{js,ts}"],
 | 
					 | 
				
			||||||
        entry: [
 | 
					 | 
				
			||||||
            ".storybook/{manager,preview}.{js,jsx,ts,tsx}",
 | 
					 | 
				
			||||||
            "**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))",
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        project: [".storybook/**/*.{js,jsx,ts,tsx}"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "eslint": {
 | 
					 | 
				
			||||||
        entry: [
 | 
					 | 
				
			||||||
            "eslint.config.mjs",
 | 
					 | 
				
			||||||
            "scripts/eslint.precommit.mjs",
 | 
					 | 
				
			||||||
            "scripts/eslint.nightmare.mjs",
 | 
					 | 
				
			||||||
            "scripts/eslint-precommit.mjs",
 | 
					 | 
				
			||||||
            "scripts/eslint-nightmare.mjs",
 | 
					 | 
				
			||||||
            "scripts/eslint.mjs",
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        config: ["package.json"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "webdriver-io": {
 | 
					 | 
				
			||||||
        config: ["wdio.conf.js"],
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default config;
 | 
					 | 
				
			||||||
							
								
								
									
										790
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										790
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -4,7 +4,7 @@
 | 
				
			|||||||
    "private": true,
 | 
					    "private": true,
 | 
				
			||||||
    "license": "MIT",
 | 
					    "license": "MIT",
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "@goauthentik/api": "^2024.8.3-1727449099",
 | 
					        "@goauthentik/api": "^2024.6.3-1724414734",
 | 
				
			||||||
        "base64-js": "^1.5.1",
 | 
					        "base64-js": "^1.5.1",
 | 
				
			||||||
        "bootstrap": "^4.6.1",
 | 
					        "bootstrap": "^4.6.1",
 | 
				
			||||||
        "formdata-polyfill": "^4.0.10",
 | 
					        "formdata-polyfill": "^4.0.10",
 | 
				
			||||||
@ -16,13 +16,13 @@
 | 
				
			|||||||
        "watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
 | 
					        "watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "devDependencies": {
 | 
					    "devDependencies": {
 | 
				
			||||||
        "@rollup/plugin-commonjs": "^28.0.0",
 | 
					        "@rollup/plugin-commonjs": "^26.0.1",
 | 
				
			||||||
        "@rollup/plugin-node-resolve": "^15.3.0",
 | 
					        "@rollup/plugin-node-resolve": "^15.2.3",
 | 
				
			||||||
        "@rollup/plugin-swc": "^0.4.0",
 | 
					        "@rollup/plugin-swc": "^0.3.1",
 | 
				
			||||||
        "@swc/cli": "^0.4.0",
 | 
					        "@swc/cli": "^0.4.0",
 | 
				
			||||||
        "@swc/core": "^1.7.28",
 | 
					        "@swc/core": "^1.7.18",
 | 
				
			||||||
        "@types/jquery": "^3.5.31",
 | 
					        "@types/jquery": "^3.5.30",
 | 
				
			||||||
        "rollup": "^4.22.5",
 | 
					        "rollup": "^4.21.0",
 | 
				
			||||||
        "rollup-plugin-copy": "^3.5.0"
 | 
					        "rollup-plugin-copy": "^3.5.0"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -21,20 +21,10 @@ export class ApplicationWizardAuthenticationMethodChoice extends WithLicenseSumm
 | 
				
			|||||||
        const selectedTypes = providerModelsList.filter(
 | 
					        const selectedTypes = providerModelsList.filter(
 | 
				
			||||||
            (t) => t.formName === this.wizard.providerModel,
 | 
					            (t) => t.formName === this.wizard.providerModel,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // As a hack, the Application wizard has separate provider paths for our three types of
 | 
					 | 
				
			||||||
        // proxy providers. This patch swaps the form we want to be directed to on page 3 from the
 | 
					 | 
				
			||||||
        // modelName to the formName, so we get the right one.  This information isn't modified
 | 
					 | 
				
			||||||
        // or forwarded, so the proxy-plus-subtype is correctly mapped on submission.
 | 
					 | 
				
			||||||
        const typesForWizard = providerModelsList.map((provider) => ({
 | 
					 | 
				
			||||||
            ...provider,
 | 
					 | 
				
			||||||
            modelName: provider.formName,
 | 
					 | 
				
			||||||
        }));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return providerModelsList.length > 0
 | 
					        return providerModelsList.length > 0
 | 
				
			||||||
            ? html`<form class="pf-c-form pf-m-horizontal">
 | 
					            ? html`<form class="pf-c-form pf-m-horizontal">
 | 
				
			||||||
                  <ak-wizard-page-type-create
 | 
					                  <ak-wizard-page-type-create
 | 
				
			||||||
                      .types=${typesForWizard}
 | 
					                      .types=${providerModelsList}
 | 
				
			||||||
                      layout=${TypeCreateWizardPageLayouts.grid}
 | 
					                      layout=${TypeCreateWizardPageLayouts.grid}
 | 
				
			||||||
                      .selectedType=${selectedTypes.length > 0 ? selectedTypes[0] : undefined}
 | 
					                      .selectedType=${selectedTypes.length > 0 ? selectedTypes[0] : undefined}
 | 
				
			||||||
                      @select=${(ev: CustomEvent<LocalTypeCreate>) => {
 | 
					                      @select=${(ev: CustomEvent<LocalTypeCreate>) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -117,7 +117,7 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
 | 
				
			|||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <p class="pf-c-form__helper-text">
 | 
					                        <p class="pf-c-form__helper-text">
 | 
				
			||||||
                            ${msg(
 | 
					                            ${msg(
 | 
				
			||||||
                                "Matches Event's Client IP (strict matching, for network matching use an Expression Policy).",
 | 
					                                "Matches Event's Client IP (strict matching, for network matching use an Expression Policy.",
 | 
				
			||||||
                            )}
 | 
					                            )}
 | 
				
			||||||
                        </p>
 | 
					                        </p>
 | 
				
			||||||
                    </ak-form-element-horizontal>
 | 
					                    </ak-form-element-horizontal>
 | 
				
			||||||
 | 
				
			|||||||
@ -162,6 +162,7 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
 | 
				
			|||||||
                <ak-flow-search
 | 
					                <ak-flow-search
 | 
				
			||||||
                    flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
					                    flowType=${FlowsInstancesListDesignationEnum.Authentication}
 | 
				
			||||||
                    .currentFlow=${provider?.authenticationFlow}
 | 
					                    .currentFlow=${provider?.authenticationFlow}
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
                ></ak-flow-search>
 | 
					                ></ak-flow-search>
 | 
				
			||||||
                <p class="pf-c-form__helper-text">
 | 
					                <p class="pf-c-form__helper-text">
 | 
				
			||||||
                    ${msg("Flow used when a user access this provider and is not authenticated.")}
 | 
					                    ${msg("Flow used when a user access this provider and is not authenticated.")}
 | 
				
			||||||
@ -170,7 +171,7 @@ export class OAuth2ProviderFormPage extends BaseProviderForm<OAuth2Provider> {
 | 
				
			|||||||
            <ak-form-element-horizontal
 | 
					            <ak-form-element-horizontal
 | 
				
			||||||
                name="authorizationFlow"
 | 
					                name="authorizationFlow"
 | 
				
			||||||
                label=${msg("Authorization flow")}
 | 
					                label=${msg("Authorization flow")}
 | 
				
			||||||
                required
 | 
					                ?required=${true}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                <ak-flow-search
 | 
					                <ak-flow-search
 | 
				
			||||||
                    flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
					                    flowType=${FlowsInstancesListDesignationEnum.Authorization}
 | 
				
			||||||
 | 
				
			|||||||
@ -327,7 +327,7 @@ export class LDAPSourceForm extends BaseSourceForm<LDAPSource> {
 | 
				
			|||||||
            <ak-form-group>
 | 
					            <ak-form-group>
 | 
				
			||||||
                <span slot="header"> ${msg("Additional settings")} </span>
 | 
					                <span slot="header"> ${msg("Additional settings")} </span>
 | 
				
			||||||
                <div slot="body" class="pf-c-form">
 | 
					                <div slot="body" class="pf-c-form">
 | 
				
			||||||
                    <ak-form-element-horizontal label=${msg("Parent Group")} name="syncParentGroup">
 | 
					                    <ak-form-element-horizontal label=${msg("Group")} name="syncParentGroup">
 | 
				
			||||||
                        <ak-search-select
 | 
					                        <ak-search-select
 | 
				
			||||||
                            .fetchObjects=${async (query?: string): Promise<Group[]> => {
 | 
					                            .fetchObjects=${async (query?: string): Promise<Group[]> => {
 | 
				
			||||||
                                const args: CoreGroupsListRequest = {
 | 
					                                const args: CoreGroupsListRequest = {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
 | 
				
			|||||||
export const ERROR_CLASS = "pf-m-danger";
 | 
					export const ERROR_CLASS = "pf-m-danger";
 | 
				
			||||||
export const PROGRESS_CLASS = "pf-m-in-progress";
 | 
					export const PROGRESS_CLASS = "pf-m-in-progress";
 | 
				
			||||||
export const CURRENT_CLASS = "pf-m-current";
 | 
					export const CURRENT_CLASS = "pf-m-current";
 | 
				
			||||||
export const VERSION = "2024.8.3";
 | 
					export const VERSION = "2024.8.2";
 | 
				
			||||||
export const TITLE_DEFAULT = "authentik";
 | 
					export const TITLE_DEFAULT = "authentik";
 | 
				
			||||||
export const ROUTE_SEPARATOR = ";";
 | 
					export const ROUTE_SEPARATOR = ";";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -155,7 +155,7 @@ select[multiple] option:checked {
 | 
				
			|||||||
    background-color: var(--ak-dark-background-light);
 | 
					    background-color: var(--ak-dark-background-light);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.pf-c-form-control[readonly] {
 | 
					.pf-c-form-control[readonly] {
 | 
				
			||||||
    background-color: var(--ak-dark-background-light) !important;
 | 
					    background-color: var(--ak-dark-background-light);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.pf-c-switch__input:checked ~ .pf-c-switch__label {
 | 
					.pf-c-switch__input:checked ~ .pf-c-switch__label {
 | 
				
			||||||
    --pf-c-switch__input--checked__label--Color: var(--ak-dark-foreground);
 | 
					    --pf-c-switch__input--checked__label--Color: var(--ak-dark-foreground);
 | 
				
			||||||
 | 
				
			|||||||
@ -513,14 +513,16 @@ ${JSON.stringify(value.new_value, null, 4)}</pre
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderUpdateAvailable() {
 | 
					    renderUpdateAvailable() {
 | 
				
			||||||
        let url = `https://github.com/goauthentik/authentik/releases/tag/version%2F${this.event.context.new_version}`;
 | 
					 | 
				
			||||||
        if (this.event.context.changelog) {
 | 
					 | 
				
			||||||
            url = this.event.context.changelog as string;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return html`<div class="pf-c-card__title">${msg("New version available")}</div>
 | 
					        return html`<div class="pf-c-card__title">${msg("New version available")}</div>
 | 
				
			||||||
            <div class="pf-c-card__body">
 | 
					            <a
 | 
				
			||||||
                <a target="_blank" href=${url}> ${this.event.context.new_version} </a>
 | 
					                target="_blank"
 | 
				
			||||||
            </div>`;
 | 
					                href="https://github.com/goauthentik/authentik/releases/tag/version%2F${this.event
 | 
				
			||||||
 | 
					                    .context.new_version}"
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                ${this.event.context.new_version}
 | 
				
			||||||
 | 
					            </a>`;
 | 
				
			||||||
 | 
					        // Action types which typically don't record any extra context.
 | 
				
			||||||
 | 
					        // If context is not empty, we fall to the default response.
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderLogin() {
 | 
					    renderLogin() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { $, browser, expect } from "@wdio/globals";
 | 
					import { $, browser } from "@wdio/globals";
 | 
				
			||||||
import { slug } from "github-slugger";
 | 
					import { slug } from "github-slugger";
 | 
				
			||||||
import { Key } from "webdriverio";
 | 
					import { Key } from "webdriverio";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
import { bound } from "@goauthentik/elements/decorators/bound.js";
 | 
					import { bound } from "@goauthentik/elements/decorators/bound.js";
 | 
				
			||||||
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
 | 
					import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
 | 
				
			||||||
import { $, browser, expect } from "@wdio/globals";
 | 
					import { $, browser } from "@wdio/globals";
 | 
				
			||||||
import { slug } from "github-slugger";
 | 
					import { slug } from "github-slugger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { html, render } from "lit";
 | 
					import { html, render } from "lit";
 | 
				
			||||||
 | 
				
			|||||||
@ -69,19 +69,9 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderGrid(): TemplateResult {
 | 
					    renderGrid(): TemplateResult {
 | 
				
			||||||
        return html`<div
 | 
					        return html`<div class="pf-l-grid pf-m-gutter">
 | 
				
			||||||
            class="pf-l-grid pf-m-gutter"
 | 
					 | 
				
			||||||
            data-ouid-component-type="ak-type-create-grid"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            ${this.types.map((type, idx) => {
 | 
					            ${this.types.map((type, idx) => {
 | 
				
			||||||
                const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
 | 
					                const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
 | 
				
			||||||
 | 
					 | 
				
			||||||
                // It's valid to pass in a local modelName or the full name with application
 | 
					 | 
				
			||||||
                // part.  If the latter, we only want the part after the dot to appear as our
 | 
					 | 
				
			||||||
                // OUIA tag for test automation.
 | 
					 | 
				
			||||||
                const componentName = type.modelName.includes(".")
 | 
					 | 
				
			||||||
                    ? (type.modelName.split(".")[1] ?? "--unknown--")
 | 
					 | 
				
			||||||
                    : type.modelName;
 | 
					 | 
				
			||||||
                return html`<div
 | 
					                return html`<div
 | 
				
			||||||
                    class="pf-l-grid__item pf-m-3-col pf-c-card ${requiresEnterprise
 | 
					                    class="pf-l-grid__item pf-m-3-col pf-c-card ${requiresEnterprise
 | 
				
			||||||
                        ? "pf-m-non-selectable-raised"
 | 
					                        ? "pf-m-non-selectable-raised"
 | 
				
			||||||
@ -89,8 +79,6 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
 | 
				
			|||||||
                        ? "pf-m-selected-raised"
 | 
					                        ? "pf-m-selected-raised"
 | 
				
			||||||
                        : ""}"
 | 
					                        : ""}"
 | 
				
			||||||
                    tabindex=${idx}
 | 
					                    tabindex=${idx}
 | 
				
			||||||
                    data-ouid-component-type="ak-type-create-grid-card"
 | 
					 | 
				
			||||||
                    data-ouid-component-name=${componentName}
 | 
					 | 
				
			||||||
                    @click=${() => {
 | 
					                    @click=${() => {
 | 
				
			||||||
                        if (requiresEnterprise) {
 | 
					                        if (requiresEnterprise) {
 | 
				
			||||||
                            return;
 | 
					                            return;
 | 
				
			||||||
@ -119,17 +107,10 @@ export class TypeCreateWizardPage extends WithLicenseSummary(WizardPage) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderList(): TemplateResult {
 | 
					    renderList(): TemplateResult {
 | 
				
			||||||
        return html`<form
 | 
					        return html`<form class="pf-c-form pf-m-horizontal">
 | 
				
			||||||
            class="pf-c-form pf-m-horizontal"
 | 
					 | 
				
			||||||
            data-ouid-component-type="ak-type-create-list"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
            ${this.types.map((type) => {
 | 
					            ${this.types.map((type) => {
 | 
				
			||||||
                const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
 | 
					                const requiresEnterprise = type.requiresEnterprise && !this.hasEnterpriseLicense;
 | 
				
			||||||
                return html`<div
 | 
					                return html`<div class="pf-c-radio">
 | 
				
			||||||
                    class="pf-c-radio"
 | 
					 | 
				
			||||||
                    data-ouid-component-type="ak-type-create-list-card"
 | 
					 | 
				
			||||||
                    data-ouid-component-name=${type.modelName.split(".")[1] ?? "--unknown--"}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <input
 | 
					                    <input
 | 
				
			||||||
                        class="pf-c-radio__input"
 | 
					                        class="pf-c-radio__input"
 | 
				
			||||||
                        type="radio"
 | 
					                        type="radio"
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user