Merge branch 'main' into web/use-oauth-by-jens
* main: (179 commits) website/integrations: add offline_access scope to DokuWiki (#10208) api: use custom json renderer for speed (#9977) core: bump github.com/sethvargo/go-envconfig from 1.0.3 to 1.1.0 (#10405) core: bump goauthentik.io/api/v3 from 3.2024060.5 to 3.2024060.6 (#10404) web: bump glob from 10.4.2 to 10.4.3 in /web (#10407) web: bump @swc/core from 1.6.7 to 1.6.13 in /web/sfe (#10408) core: bump ruff from 0.5.0 to 0.5.1 (#10390) web: bump @swc/cli from 0.3.14 to 0.4.0 in /web/sfe (#10394) core: bump certifi from 2024.2.2 to 2024.7.4 (#10398) web: bump @swc/core from 1.6.6 to 1.6.7 in /web/sfe (#10395) web: bump @sentry/browser from 8.14.0 to 8.15.0 in /web in the sentry group across 1 directory (#10388) website/integrations: aws: cleanup (#10355) web: bump API Client version (#10389) web/flows: Simplified flow executor (#10296) website/docs: sources: ldap: remove extra example (#10387) website/docs: add new content from old PR #9524 (#10158) stages/authenticator_validate: fix friendly_name being required (#10382) core: bump go api client (#10383) web: bump API Client version (#10381) outposts: make refresh interval configurable (#10138) ...
This commit is contained in:
		@ -1,5 +1,5 @@
 | 
				
			|||||||
[bumpversion]
 | 
					[bumpversion]
 | 
				
			||||||
current_version = 2024.4.2
 | 
					current_version = 2024.6.0
 | 
				
			||||||
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*))?
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										38
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@ -21,7 +21,10 @@ updates:
 | 
				
			|||||||
    labels:
 | 
					    labels:
 | 
				
			||||||
      - dependencies
 | 
					      - dependencies
 | 
				
			||||||
  - package-ecosystem: npm
 | 
					  - package-ecosystem: npm
 | 
				
			||||||
    directory: "/web"
 | 
					    directories:
 | 
				
			||||||
 | 
					      - "/web"
 | 
				
			||||||
 | 
					      - "/tests/wdio"
 | 
				
			||||||
 | 
					      - "/web/sfe"
 | 
				
			||||||
    schedule:
 | 
					    schedule:
 | 
				
			||||||
      interval: daily
 | 
					      interval: daily
 | 
				
			||||||
      time: "04:00"
 | 
					      time: "04:00"
 | 
				
			||||||
@ -30,7 +33,6 @@ updates:
 | 
				
			|||||||
    open-pull-requests-limit: 10
 | 
					    open-pull-requests-limit: 10
 | 
				
			||||||
    commit-message:
 | 
					    commit-message:
 | 
				
			||||||
      prefix: "web:"
 | 
					      prefix: "web:"
 | 
				
			||||||
    # TODO: deduplicate these groups
 | 
					 | 
				
			||||||
    groups:
 | 
					    groups:
 | 
				
			||||||
      sentry:
 | 
					      sentry:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
@ -56,38 +58,6 @@ updates:
 | 
				
			|||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@rollup/*"
 | 
					          - "@rollup/*"
 | 
				
			||||||
          - "rollup-*"
 | 
					          - "rollup-*"
 | 
				
			||||||
  - package-ecosystem: npm
 | 
					 | 
				
			||||||
    directory: "/tests/wdio"
 | 
					 | 
				
			||||||
    schedule:
 | 
					 | 
				
			||||||
      interval: daily
 | 
					 | 
				
			||||||
      time: "04:00"
 | 
					 | 
				
			||||||
    labels:
 | 
					 | 
				
			||||||
      - dependencies
 | 
					 | 
				
			||||||
    open-pull-requests-limit: 10
 | 
					 | 
				
			||||||
    commit-message:
 | 
					 | 
				
			||||||
      prefix: "web:"
 | 
					 | 
				
			||||||
    # TODO: deduplicate these groups
 | 
					 | 
				
			||||||
    groups:
 | 
					 | 
				
			||||||
      sentry:
 | 
					 | 
				
			||||||
        patterns:
 | 
					 | 
				
			||||||
          - "@sentry/*"
 | 
					 | 
				
			||||||
          - "@spotlightjs/*"
 | 
					 | 
				
			||||||
      babel:
 | 
					 | 
				
			||||||
        patterns:
 | 
					 | 
				
			||||||
          - "@babel/*"
 | 
					 | 
				
			||||||
          - "babel-*"
 | 
					 | 
				
			||||||
      eslint:
 | 
					 | 
				
			||||||
        patterns:
 | 
					 | 
				
			||||||
          - "@typescript-eslint/*"
 | 
					 | 
				
			||||||
          - "eslint"
 | 
					 | 
				
			||||||
          - "eslint-*"
 | 
					 | 
				
			||||||
      storybook:
 | 
					 | 
				
			||||||
        patterns:
 | 
					 | 
				
			||||||
          - "@storybook/*"
 | 
					 | 
				
			||||||
          - "*storybook*"
 | 
					 | 
				
			||||||
      esbuild:
 | 
					 | 
				
			||||||
        patterns:
 | 
					 | 
				
			||||||
          - "@esbuild/*"
 | 
					 | 
				
			||||||
      wdio:
 | 
					      wdio:
 | 
				
			||||||
        patterns:
 | 
					        patterns:
 | 
				
			||||||
          - "@wdio/*"
 | 
					          - "@wdio/*"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								.github/workflows/api-ts-publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.github/workflows/api-ts-publish.yml
									
									
									
									
										vendored
									
									
								
							@ -31,7 +31,12 @@ jobs:
 | 
				
			|||||||
        env:
 | 
					        env:
 | 
				
			||||||
          NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
 | 
					          NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
 | 
				
			||||||
      - name: Upgrade /web
 | 
					      - name: Upgrade /web
 | 
				
			||||||
        working-directory: web/
 | 
					        working-directory: web
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
 | 
				
			||||||
 | 
					          npm i @goauthentik/api@$VERSION
 | 
				
			||||||
 | 
					      - name: Upgrade /web/sfe
 | 
				
			||||||
 | 
					        working-directory: web/sfe
 | 
				
			||||||
        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
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							@ -219,7 +219,7 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          ref: ${{ github.event.pull_request.head.sha }}
 | 
					          ref: ${{ github.event.pull_request.head.sha }}
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.1.0
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3
 | 
					        uses: docker/setup-buildx-action@v3
 | 
				
			||||||
      - name: prepare variables
 | 
					      - name: prepare variables
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-outpost.yml
									
									
									
									
										vendored
									
									
								
							@ -76,7 +76,7 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          ref: ${{ github.event.pull_request.head.sha }}
 | 
					          ref: ${{ github.event.pull_request.head.sha }}
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.1.0
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3
 | 
					        uses: docker/setup-buildx-action@v3
 | 
				
			||||||
      - name: prepare variables
 | 
					      - name: prepare variables
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										120
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										120
									
								
								.github/workflows/ci-web.yml
									
									
									
									
										vendored
									
									
								
							@ -12,14 +12,36 @@ on:
 | 
				
			|||||||
      - version-*
 | 
					      - version-*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  lint-eslint:
 | 
					  lint:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    strategy:
 | 
					    strategy:
 | 
				
			||||||
      fail-fast: false
 | 
					      fail-fast: false
 | 
				
			||||||
      matrix:
 | 
					      matrix:
 | 
				
			||||||
 | 
					        command:
 | 
				
			||||||
 | 
					          - lint
 | 
				
			||||||
 | 
					          - lint:lockfile
 | 
				
			||||||
 | 
					          - tsc
 | 
				
			||||||
 | 
					          - prettier-check
 | 
				
			||||||
        project:
 | 
					        project:
 | 
				
			||||||
          - web
 | 
					          - web
 | 
				
			||||||
          - tests/wdio
 | 
					          - tests/wdio
 | 
				
			||||||
 | 
					        include:
 | 
				
			||||||
 | 
					          - command: tsc
 | 
				
			||||||
 | 
					            project: web
 | 
				
			||||||
 | 
					            extra_setup: |
 | 
				
			||||||
 | 
					              cd sfe/ && npm ci
 | 
				
			||||||
 | 
					          - command: lit-analyse
 | 
				
			||||||
 | 
					            project: web
 | 
				
			||||||
 | 
					            extra_setup: |
 | 
				
			||||||
 | 
					              # lit-analyse doesn't understand path rewrites, so make it
 | 
				
			||||||
 | 
					              # belive it's an actual module
 | 
				
			||||||
 | 
					              cd node_modules/@goauthentik
 | 
				
			||||||
 | 
					              ln -s ../../src/ web
 | 
				
			||||||
 | 
					        exclude:
 | 
				
			||||||
 | 
					          - command: lint:lockfile
 | 
				
			||||||
 | 
					            project: tests/wdio
 | 
				
			||||||
 | 
					          - command: tsc
 | 
				
			||||||
 | 
					            project: tests/wdio
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
      - uses: actions/setup-node@v4
 | 
					      - uses: actions/setup-node@v4
 | 
				
			||||||
@ -28,85 +50,17 @@ jobs:
 | 
				
			|||||||
          cache: "npm"
 | 
					          cache: "npm"
 | 
				
			||||||
          cache-dependency-path: ${{ matrix.project }}/package-lock.json
 | 
					          cache-dependency-path: ${{ matrix.project }}/package-lock.json
 | 
				
			||||||
      - working-directory: ${{ matrix.project }}/
 | 
					      - working-directory: ${{ matrix.project }}/
 | 
				
			||||||
        run: npm ci
 | 
					 | 
				
			||||||
      - name: Generate API
 | 
					 | 
				
			||||||
        run: make gen-client-ts
 | 
					 | 
				
			||||||
      - name: Eslint
 | 
					 | 
				
			||||||
        working-directory: ${{ matrix.project }}/
 | 
					 | 
				
			||||||
        run: npm run lint
 | 
					 | 
				
			||||||
  lint-lockfile:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					 | 
				
			||||||
      - working-directory: web/
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          [ -z "$(jq -r '.packages | to_entries[] | select((.key | startswith("node_modules")) and (.value | has("resolved") | not)) | .key' < package-lock.json)" ]
 | 
					 | 
				
			||||||
  lint-build:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					 | 
				
			||||||
      - uses: actions/setup-node@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          node-version-file: web/package.json
 | 
					 | 
				
			||||||
          cache: "npm"
 | 
					 | 
				
			||||||
          cache-dependency-path: web/package-lock.json
 | 
					 | 
				
			||||||
      - working-directory: web/
 | 
					 | 
				
			||||||
        run: npm ci
 | 
					 | 
				
			||||||
      - name: Generate API
 | 
					 | 
				
			||||||
        run: make gen-client-ts
 | 
					 | 
				
			||||||
      - name: TSC
 | 
					 | 
				
			||||||
        working-directory: web/
 | 
					 | 
				
			||||||
        run: npm run tsc
 | 
					 | 
				
			||||||
  lint-prettier:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    strategy:
 | 
					 | 
				
			||||||
      fail-fast: false
 | 
					 | 
				
			||||||
      matrix:
 | 
					 | 
				
			||||||
        project:
 | 
					 | 
				
			||||||
          - web
 | 
					 | 
				
			||||||
          - tests/wdio
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					 | 
				
			||||||
      - uses: actions/setup-node@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          node-version-file: ${{ matrix.project }}/package.json
 | 
					 | 
				
			||||||
          cache: "npm"
 | 
					 | 
				
			||||||
          cache-dependency-path: ${{ matrix.project }}/package-lock.json
 | 
					 | 
				
			||||||
      - working-directory: ${{ matrix.project }}/
 | 
					 | 
				
			||||||
        run: npm ci
 | 
					 | 
				
			||||||
      - name: Generate API
 | 
					 | 
				
			||||||
        run: make gen-client-ts
 | 
					 | 
				
			||||||
      - name: prettier
 | 
					 | 
				
			||||||
        working-directory: ${{ matrix.project }}/
 | 
					 | 
				
			||||||
        run: npm run prettier-check
 | 
					 | 
				
			||||||
  lint-lit-analyse:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					 | 
				
			||||||
      - uses: actions/setup-node@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          node-version-file: web/package.json
 | 
					 | 
				
			||||||
          cache: "npm"
 | 
					 | 
				
			||||||
          cache-dependency-path: web/package-lock.json
 | 
					 | 
				
			||||||
      - working-directory: web/
 | 
					 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          npm ci
 | 
					          npm ci
 | 
				
			||||||
          # lit-analyse doesn't understand path rewrites, so make it
 | 
					          ${{ matrix.extra_setup }}
 | 
				
			||||||
          # belive it's an actual module
 | 
					 | 
				
			||||||
          cd node_modules/@goauthentik
 | 
					 | 
				
			||||||
          ln -s ../../src/ web
 | 
					 | 
				
			||||||
      - name: Generate API
 | 
					      - name: Generate API
 | 
				
			||||||
        run: make gen-client-ts
 | 
					        run: make gen-client-ts
 | 
				
			||||||
      - name: lit-analyse
 | 
					      - name: Lint
 | 
				
			||||||
        working-directory: web/
 | 
					        working-directory: ${{ matrix.project }}/
 | 
				
			||||||
        run: npm run lit-analyse
 | 
					        run: npm run ${{ matrix.command }}
 | 
				
			||||||
  ci-web-mark:
 | 
					  ci-web-mark:
 | 
				
			||||||
    needs:
 | 
					    needs:
 | 
				
			||||||
      - lint-lockfile
 | 
					      - lint
 | 
				
			||||||
      - lint-eslint
 | 
					 | 
				
			||||||
      - lint-prettier
 | 
					 | 
				
			||||||
      - lint-lit-analyse
 | 
					 | 
				
			||||||
      - lint-build
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - run: echo mark
 | 
					      - run: echo mark
 | 
				
			||||||
@ -128,3 +82,21 @@ jobs:
 | 
				
			|||||||
      - name: build
 | 
					      - name: build
 | 
				
			||||||
        working-directory: web/
 | 
					        working-directory: web/
 | 
				
			||||||
        run: npm run build
 | 
					        run: npm run build
 | 
				
			||||||
 | 
					  test:
 | 
				
			||||||
 | 
					    needs:
 | 
				
			||||||
 | 
					      - ci-web-mark
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
 | 
					      - uses: actions/setup-node@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          node-version-file: web/package.json
 | 
				
			||||||
 | 
					          cache: "npm"
 | 
				
			||||||
 | 
					          cache-dependency-path: web/package-lock.json
 | 
				
			||||||
 | 
					      - working-directory: web/
 | 
				
			||||||
 | 
					        run: npm ci
 | 
				
			||||||
 | 
					      - name: Generate API
 | 
				
			||||||
 | 
					        run: make gen-client-ts
 | 
				
			||||||
 | 
					      - name: test
 | 
				
			||||||
 | 
					        working-directory: web/
 | 
				
			||||||
 | 
					        run: npm run test
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								.github/workflows/ci-website.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								.github/workflows/ci-website.yml
									
									
									
									
										vendored
									
									
								
							@ -12,27 +12,21 @@ on:
 | 
				
			|||||||
      - version-*
 | 
					      - version-*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobs:
 | 
					jobs:
 | 
				
			||||||
  lint-lockfile:
 | 
					  lint:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        command:
 | 
				
			||||||
 | 
					          - lint:lockfile
 | 
				
			||||||
 | 
					          - prettier-check
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
      - working-directory: website/
 | 
					 | 
				
			||||||
        run: |
 | 
					 | 
				
			||||||
          [ -z "$(jq -r '.packages | to_entries[] | select((.key | startswith("node_modules")) and (.value | has("resolved") | not)) | .key' < package-lock.json)" ]
 | 
					 | 
				
			||||||
  lint-prettier:
 | 
					 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					 | 
				
			||||||
    steps:
 | 
					 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					 | 
				
			||||||
      - uses: actions/setup-node@v4
 | 
					 | 
				
			||||||
        with:
 | 
					 | 
				
			||||||
          node-version-file: website/package.json
 | 
					 | 
				
			||||||
          cache: "npm"
 | 
					 | 
				
			||||||
          cache-dependency-path: website/package-lock.json
 | 
					 | 
				
			||||||
      - working-directory: website/
 | 
					      - working-directory: website/
 | 
				
			||||||
        run: npm ci
 | 
					        run: npm ci
 | 
				
			||||||
      - name: prettier
 | 
					      - name: Lint
 | 
				
			||||||
        working-directory: website/
 | 
					        working-directory: website/
 | 
				
			||||||
        run: npm run prettier-check
 | 
					        run: npm run ${{ matrix.command }}
 | 
				
			||||||
  test:
 | 
					  test:
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
@ -69,8 +63,7 @@ jobs:
 | 
				
			|||||||
        run: npm run ${{ matrix.job }}
 | 
					        run: npm run ${{ matrix.job }}
 | 
				
			||||||
  ci-website-mark:
 | 
					  ci-website-mark:
 | 
				
			||||||
    needs:
 | 
					    needs:
 | 
				
			||||||
      - lint-lockfile
 | 
					      - lint
 | 
				
			||||||
      - lint-prettier
 | 
					 | 
				
			||||||
      - test
 | 
					      - test
 | 
				
			||||||
      - build
 | 
					      - build
 | 
				
			||||||
    runs-on: ubuntu-latest
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								.github/workflows/release-publish.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/release-publish.yml
									
									
									
									
										vendored
									
									
								
							@ -14,7 +14,7 @@ jobs:
 | 
				
			|||||||
    steps:
 | 
					    steps:
 | 
				
			||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.1.0
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3
 | 
					        uses: docker/setup-buildx-action@v3
 | 
				
			||||||
      - name: prepare variables
 | 
					      - name: prepare variables
 | 
				
			||||||
@ -68,7 +68,7 @@ jobs:
 | 
				
			|||||||
        with:
 | 
					        with:
 | 
				
			||||||
          go-version-file: "go.mod"
 | 
					          go-version-file: "go.mod"
 | 
				
			||||||
      - name: Set up QEMU
 | 
					      - name: Set up QEMU
 | 
				
			||||||
        uses: docker/setup-qemu-action@v3.0.0
 | 
					        uses: docker/setup-qemu-action@v3.1.0
 | 
				
			||||||
      - name: Set up Docker Buildx
 | 
					      - name: Set up Docker Buildx
 | 
				
			||||||
        uses: docker/setup-buildx-action@v3
 | 
					        uses: docker/setup-buildx-action@v3
 | 
				
			||||||
      - name: prepare variables
 | 
					      - name: prepare variables
 | 
				
			||||||
@ -155,8 +155,8 @@ jobs:
 | 
				
			|||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
      - name: Run test suite in final docker images
 | 
					      - name: Run test suite in final docker images
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          echo "PG_PASS=$(openssl rand 32 | base64)" >> .env
 | 
					          echo "PG_PASS=$(openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
          echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64)" >> .env
 | 
					          echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
          docker compose pull -q
 | 
					          docker compose pull -q
 | 
				
			||||||
          docker compose up --no-start
 | 
					          docker compose up --no-start
 | 
				
			||||||
          docker compose start postgresql redis
 | 
					          docker compose start postgresql redis
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								.github/workflows/release-tag.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release-tag.yml
									
									
									
									
										vendored
									
									
								
							@ -14,8 +14,8 @@ jobs:
 | 
				
			|||||||
      - uses: actions/checkout@v4
 | 
					      - uses: actions/checkout@v4
 | 
				
			||||||
      - name: Pre-release test
 | 
					      - name: Pre-release test
 | 
				
			||||||
        run: |
 | 
					        run: |
 | 
				
			||||||
          echo "PG_PASS=$(openssl rand 32 | base64)" >> .env
 | 
					          echo "PG_PASS=$(openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
          echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64)" >> .env
 | 
					          echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
          docker buildx install
 | 
					          docker buildx install
 | 
				
			||||||
          mkdir -p ./gen-ts-api
 | 
					          mkdir -p ./gen-ts-api
 | 
				
			||||||
          docker build -t testing:latest .
 | 
					          docker build -t testing:latest .
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Dockerfile
									
									
									
									
									
								
							@ -22,13 +22,20 @@ RUN npm run build-bundled
 | 
				
			|||||||
# Stage 2: Build webui
 | 
					# Stage 2: Build webui
 | 
				
			||||||
FROM --platform=${BUILDPLATFORM} docker.io/node:22 as web-builder
 | 
					FROM --platform=${BUILDPLATFORM} docker.io/node:22 as web-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ARG GIT_BUILD_HASH
 | 
				
			||||||
 | 
					ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
 | 
				
			||||||
ENV NODE_ENV=production
 | 
					ENV NODE_ENV=production
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR /work/web
 | 
					WORKDIR /work/web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
 | 
					RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
 | 
				
			||||||
    --mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
 | 
					    --mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
 | 
				
			||||||
 | 
					    --mount=type=bind,target=/work/web/sfe/package.json,src=./web/sfe/package.json \
 | 
				
			||||||
 | 
					    --mount=type=bind,target=/work/web/sfe/package-lock.json,src=./web/sfe/package-lock.json \
 | 
				
			||||||
 | 
					    --mount=type=bind,target=/work/web/scripts,src=./web/scripts \
 | 
				
			||||||
    --mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
 | 
					    --mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
 | 
				
			||||||
 | 
					    npm ci --include=dev && \
 | 
				
			||||||
 | 
					    cd sfe && \
 | 
				
			||||||
    npm ci --include=dev
 | 
					    npm ci --include=dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY ./package.json /work
 | 
					COPY ./package.json /work
 | 
				
			||||||
@ -36,7 +43,9 @@ COPY ./web /work/web/
 | 
				
			|||||||
COPY ./website /work/website/
 | 
					COPY ./website /work/website/
 | 
				
			||||||
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
 | 
					COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN npm run build
 | 
					RUN npm run build && \
 | 
				
			||||||
 | 
					    cd sfe && \
 | 
				
			||||||
 | 
					    npm run build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Stage 3: Build go proxy
 | 
					# Stage 3: Build go proxy
 | 
				
			||||||
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS go-builder
 | 
					FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS go-builder
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Makefile
									
									
									
									
									
								
							@ -47,8 +47,8 @@ test-go:
 | 
				
			|||||||
	go test -timeout 0 -v -race -cover ./...
 | 
						go test -timeout 0 -v -race -cover ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test-docker:  ## Run all tests in a docker-compose
 | 
					test-docker:  ## Run all tests in a docker-compose
 | 
				
			||||||
	echo "PG_PASS=$(shell openssl rand 32 | base64)" >> .env
 | 
						echo "PG_PASS=$(shell openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
	echo "AUTHENTIK_SECRET_KEY=$(shell openssl rand 32 | base64)" >> .env
 | 
						echo "AUTHENTIK_SECRET_KEY=$(shell openssl rand 32 | base64 -w 0)" >> .env
 | 
				
			||||||
	docker compose pull -q
 | 
						docker compose pull -q
 | 
				
			||||||
	docker compose up --no-start
 | 
						docker compose up --no-start
 | 
				
			||||||
	docker compose start postgresql redis
 | 
						docker compose start postgresql redis
 | 
				
			||||||
@ -60,9 +60,11 @@ test: ## Run the server tests and produce a coverage report (locally)
 | 
				
			|||||||
	coverage html
 | 
						coverage html
 | 
				
			||||||
	coverage report
 | 
						coverage report
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lint-fix:  ## Lint and automatically fix errors in the python source code. Reports spelling errors.
 | 
					lint-fix: lint-codespell  ## Lint and automatically fix errors in the python source code. Reports spelling errors.
 | 
				
			||||||
	black $(PY_SOURCES)
 | 
						black $(PY_SOURCES)
 | 
				
			||||||
	ruff check --fix $(PY_SOURCES)
 | 
						ruff check --fix $(PY_SOURCES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lint-codespell:  ## Reports spelling errors.
 | 
				
			||||||
	codespell -w $(CODESPELL_ARGS)
 | 
						codespell -w $(CODESPELL_ARGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lint: ## Lint the python and golang sources
 | 
					lint: ## Lint the python and golang sources
 | 
				
			||||||
@ -239,7 +241,7 @@ website: website-lint-fix website-build  ## Automatically fix formatting issues
 | 
				
			|||||||
website-install:
 | 
					website-install:
 | 
				
			||||||
	cd website && npm ci
 | 
						cd website && npm ci
 | 
				
			||||||
 | 
					
 | 
				
			||||||
website-lint-fix:
 | 
					website-lint-fix: lint-codespell
 | 
				
			||||||
	cd website && npm run prettier
 | 
						cd website && npm run prettier
 | 
				
			||||||
 | 
					
 | 
				
			||||||
website-build:
 | 
					website-build:
 | 
				
			||||||
 | 
				
			|||||||
@ -18,10 +18,10 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
(.x being the latest patch release for each version)
 | 
					(.x being the latest patch release for each version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Version   | Supported |
 | 
					| Version  | Supported |
 | 
				
			||||||
| --------- | --------- |
 | 
					| -------- | --------- |
 | 
				
			||||||
| 2023.10.x | ✅        |
 | 
					| 2024.4.x | ✅        |
 | 
				
			||||||
| 2024.2.x  | ✅        |
 | 
					| 2024.6.x | ✅        |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Reporting a Vulnerability
 | 
					## Reporting a Vulnerability
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from os import environ
 | 
					from os import environ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__version__ = "2024.4.2"
 | 
					__version__ = "2024.6.0"
 | 
				
			||||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
 | 
					ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ from authentik.tenants.utils import get_current_tenant
 | 
				
			|||||||
class FooterLinkSerializer(PassiveSerializer):
 | 
					class FooterLinkSerializer(PassiveSerializer):
 | 
				
			||||||
    """Links returned in Config API"""
 | 
					    """Links returned in Config API"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    href = CharField(read_only=True)
 | 
					    href = CharField(read_only=True, allow_null=True)
 | 
				
			||||||
    name = CharField(read_only=True)
 | 
					    name = CharField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -103,7 +103,7 @@ class ApplicationSerializer(ModelSerializer):
 | 
				
			|||||||
class ApplicationViewSet(UsedByMixin, ModelViewSet):
 | 
					class ApplicationViewSet(UsedByMixin, ModelViewSet):
 | 
				
			||||||
    """Application Viewset"""
 | 
					    """Application Viewset"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    queryset = Application.objects.all().prefetch_related("provider")
 | 
					    queryset = Application.objects.all().prefetch_related("provider").prefetch_related("policies")
 | 
				
			||||||
    serializer_class = ApplicationSerializer
 | 
					    serializer_class = ApplicationSerializer
 | 
				
			||||||
    search_fields = [
 | 
					    search_fields = [
 | 
				
			||||||
        "name",
 | 
					        "name",
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,13 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
 | 
				
			|||||||
        if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
 | 
					        if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
 | 
				
			||||||
            self.fields["key"] = CharField(required=False)
 | 
					            self.fields["key"] = CharField(required=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def validate_user(self, user: User):
 | 
				
			||||||
 | 
					        """Ensure user of token cannot be changed"""
 | 
				
			||||||
 | 
					        if self.instance and self.instance.user_id:
 | 
				
			||||||
 | 
					            if user.pk != self.instance.user_id:
 | 
				
			||||||
 | 
					                raise ValidationError("User cannot be changed")
 | 
				
			||||||
 | 
					        return user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
 | 
					    def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
 | 
				
			||||||
        """Ensure only API or App password tokens are created."""
 | 
					        """Ensure only API or App password tokens are created."""
 | 
				
			||||||
        request: Request = self.context.get("request")
 | 
					        request: Request = self.context.get("request")
 | 
				
			||||||
 | 
				
			|||||||
@ -76,8 +76,11 @@ class PropertyMappingEvaluator(BaseEvaluator):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        if "request" in self._context:
 | 
					        if "request" in self._context:
 | 
				
			||||||
            req: PolicyRequest = self._context["request"]
 | 
					            req: PolicyRequest = self._context["request"]
 | 
				
			||||||
            event.from_http(req.http_request, req.user)
 | 
					            if req.http_request:
 | 
				
			||||||
            return
 | 
					                event.from_http(req.http_request, req.user)
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            elif req.user:
 | 
				
			||||||
 | 
					                event.set_user(req.user)
 | 
				
			||||||
        event.save()
 | 
					        event.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def evaluate(self, *args, **kwargs) -> Any:
 | 
					    def evaluate(self, *args, **kwargs) -> Any:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
"""authentik core exceptions"""
 | 
					"""authentik core exceptions"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.lib.expression.exceptions import ControlFlowException
 | 
				
			||||||
from authentik.lib.sentry import SentryIgnoredException
 | 
					from authentik.lib.sentry import SentryIgnoredException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,7 +13,7 @@ class PropertyMappingExpressionException(SentryIgnoredException):
 | 
				
			|||||||
        self.mapping = mapping
 | 
					        self.mapping = mapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SkipObjectException(PropertyMappingExpressionException):
 | 
					class SkipObjectException(ControlFlowException):
 | 
				
			||||||
    """Exception which can be raised in a property mapping to skip syncing an object.
 | 
					    """Exception which can be raised in a property mapping to skip syncing an object.
 | 
				
			||||||
    Only applies to Property mappings which sync objects, and not on mappings which transitively
 | 
					    Only applies to Property mappings which sync objects, and not on mappings which transitively
 | 
				
			||||||
    apply to a single user"""
 | 
					    apply to a single user"""
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@ from authentik.blueprints.models import ManagedModel
 | 
				
			|||||||
from authentik.core.expression.exceptions import PropertyMappingExpressionException
 | 
					from authentik.core.expression.exceptions import PropertyMappingExpressionException
 | 
				
			||||||
from authentik.core.types import UILoginButton, UserSettingSerializer
 | 
					from authentik.core.types import UILoginButton, UserSettingSerializer
 | 
				
			||||||
from authentik.lib.avatars import get_avatar
 | 
					from authentik.lib.avatars import get_avatar
 | 
				
			||||||
 | 
					from authentik.lib.expression.exceptions import ControlFlowException
 | 
				
			||||||
from authentik.lib.generators import generate_id
 | 
					from authentik.lib.generators import generate_id
 | 
				
			||||||
from authentik.lib.models import (
 | 
					from authentik.lib.models import (
 | 
				
			||||||
    CreatedUpdatedModel,
 | 
					    CreatedUpdatedModel,
 | 
				
			||||||
@ -783,6 +784,8 @@ class PropertyMapping(SerializerModel, ManagedModel):
 | 
				
			|||||||
        evaluator = PropertyMappingEvaluator(self, user, request, **kwargs)
 | 
					        evaluator = PropertyMappingEvaluator(self, user, request, **kwargs)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            return evaluator.evaluate(self.expression)
 | 
					            return evaluator.evaluate(self.expression)
 | 
				
			||||||
 | 
					        except ControlFlowException as exc:
 | 
				
			||||||
 | 
					            raise exc
 | 
				
			||||||
        except Exception as exc:
 | 
					        except Exception as exc:
 | 
				
			||||||
            raise PropertyMappingExpressionException(self, exc) from exc
 | 
					            raise PropertyMappingExpressionException(self, exc) from exc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -309,7 +309,7 @@ class SourceFlowManager:
 | 
				
			|||||||
        # When request isn't authenticated we jump straight to auth
 | 
					        # When request isn't authenticated we jump straight to auth
 | 
				
			||||||
        if not self.request.user.is_authenticated:
 | 
					        if not self.request.user.is_authenticated:
 | 
				
			||||||
            return self.handle_auth(connection)
 | 
					            return self.handle_auth(connection)
 | 
				
			||||||
        # Connection has already been saved
 | 
					        connection.save()
 | 
				
			||||||
        Event.new(
 | 
					        Event.new(
 | 
				
			||||||
            EventAction.SOURCE_LINKED,
 | 
					            EventAction.SOURCE_LINKED,
 | 
				
			||||||
            message="Linked Source",
 | 
					            message="Linked Source",
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@
 | 
				
			|||||||
        versionSubdomain: "{{ version_subdomain }}",
 | 
					        versionSubdomain: "{{ version_subdomain }}",
 | 
				
			||||||
        build: "{{ build }}",
 | 
					        build: "{{ build }}",
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    window.addEventListener("DOMContentLoaded", () => {
 | 
					    window.addEventListener("DOMContentLoaded", function () {
 | 
				
			||||||
        {% for message in messages %}
 | 
					        {% for message in messages %}
 | 
				
			||||||
        window.dispatchEvent(
 | 
					        window.dispatchEvent(
 | 
				
			||||||
            new CustomEvent("ak-message", {
 | 
					            new CustomEvent("ak-message", {
 | 
				
			||||||
 | 
				
			|||||||
@ -71,9 +71,9 @@
 | 
				
			|||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
                <li>
 | 
					                <li>
 | 
				
			||||||
                    <a href="https://goauthentik.io?utm_source=authentik">
 | 
					                    <span>
 | 
				
			||||||
                        {% trans 'Powered by authentik' %}
 | 
					                        {% trans 'Powered by authentik' %}
 | 
				
			||||||
                    </a>
 | 
					                    </span>
 | 
				
			||||||
                </li>
 | 
					                </li>
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
        </footer>
 | 
					        </footer>
 | 
				
			||||||
 | 
				
			|||||||
@ -17,11 +17,5 @@ def versioned_script(path: str) -> str:
 | 
				
			|||||||
            f'<script src="{static_loader(path.replace("%v", get_full_version()))}'
 | 
					            f'<script src="{static_loader(path.replace("%v", get_full_version()))}'
 | 
				
			||||||
            '" type="module"></script>'
 | 
					            '" type="module"></script>'
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        # Legacy method of loading scripts used as a fallback, without the version in the filename
 | 
					 | 
				
			||||||
        # TODO: Remove after 2024.6 or later
 | 
					 | 
				
			||||||
        (
 | 
					 | 
				
			||||||
            f'<script src="{static_loader(path.replace("-%v", ""))}?'
 | 
					 | 
				
			||||||
            f'version={get_full_version()}" type="module"></script>'
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    return mark_safe("".join(returned_lines))  # nosec
 | 
					    return mark_safe("".join(returned_lines))  # nosec
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,10 @@
 | 
				
			|||||||
from django.test import RequestFactory, TestCase
 | 
					from django.test import RequestFactory, TestCase
 | 
				
			||||||
from guardian.shortcuts import get_anonymous_user
 | 
					from guardian.shortcuts import get_anonymous_user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.expression.exceptions import PropertyMappingExpressionException
 | 
					from authentik.core.expression.exceptions import (
 | 
				
			||||||
 | 
					    PropertyMappingExpressionException,
 | 
				
			||||||
 | 
					    SkipObjectException,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.core.models import PropertyMapping
 | 
					from authentik.core.models import PropertyMapping
 | 
				
			||||||
from authentik.core.tests.utils import create_test_admin_user
 | 
					from authentik.core.tests.utils import create_test_admin_user
 | 
				
			||||||
from authentik.events.models import Event, EventAction
 | 
					from authentik.events.models import Event, EventAction
 | 
				
			||||||
@ -42,6 +45,17 @@ class TestPropertyMappings(TestCase):
 | 
				
			|||||||
        self.assertTrue(events.exists())
 | 
					        self.assertTrue(events.exists())
 | 
				
			||||||
        self.assertEqual(len(events), 1)
 | 
					        self.assertEqual(len(events), 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_expression_skip(self):
 | 
				
			||||||
 | 
					        """Test expression error"""
 | 
				
			||||||
 | 
					        expr = "raise SkipObject"
 | 
				
			||||||
 | 
					        mapping = PropertyMapping.objects.create(name=generate_id(), expression=expr)
 | 
				
			||||||
 | 
					        with self.assertRaises(SkipObjectException):
 | 
				
			||||||
 | 
					            mapping.evaluate(None, None)
 | 
				
			||||||
 | 
					        events = Event.objects.filter(
 | 
				
			||||||
 | 
					            action=EventAction.PROPERTY_MAPPING_EXCEPTION, context__expression=expr
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertFalse(events.exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_expression_error_extended(self):
 | 
					    def test_expression_error_extended(self):
 | 
				
			||||||
        """Test expression error (with user and http request"""
 | 
					        """Test expression error (with user and http request"""
 | 
				
			||||||
        expr = "return aaa"
 | 
					        expr = "return aaa"
 | 
				
			||||||
 | 
				
			|||||||
@ -13,9 +13,8 @@ from authentik.core.models import (
 | 
				
			|||||||
    USER_ATTRIBUTE_TOKEN_MAXIMUM_LIFETIME,
 | 
					    USER_ATTRIBUTE_TOKEN_MAXIMUM_LIFETIME,
 | 
				
			||||||
    Token,
 | 
					    Token,
 | 
				
			||||||
    TokenIntents,
 | 
					    TokenIntents,
 | 
				
			||||||
    User,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.core.tests.utils import create_test_admin_user
 | 
					from authentik.core.tests.utils import create_test_admin_user, create_test_user
 | 
				
			||||||
from authentik.lib.generators import generate_id
 | 
					from authentik.lib.generators import generate_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -24,7 +23,7 @@ class TestTokenAPI(APITestCase):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def setUp(self) -> None:
 | 
					    def setUp(self) -> None:
 | 
				
			||||||
        super().setUp()
 | 
					        super().setUp()
 | 
				
			||||||
        self.user = User.objects.create(username="testuser")
 | 
					        self.user = create_test_user()
 | 
				
			||||||
        self.admin = create_test_admin_user()
 | 
					        self.admin = create_test_admin_user()
 | 
				
			||||||
        self.client.force_login(self.user)
 | 
					        self.client.force_login(self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -154,6 +153,24 @@ class TestTokenAPI(APITestCase):
 | 
				
			|||||||
        self.assertEqual(token.expiring, True)
 | 
					        self.assertEqual(token.expiring, True)
 | 
				
			||||||
        self.assertNotEqual(token.expires.timestamp(), expires.timestamp())
 | 
					        self.assertNotEqual(token.expires.timestamp(), expires.timestamp())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_token_change_user(self):
 | 
				
			||||||
 | 
					        """Test creating a token and then changing the user"""
 | 
				
			||||||
 | 
					        ident = generate_id()
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("authentik_api:token-list"), {"identifier": ident})
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 201)
 | 
				
			||||||
 | 
					        token = Token.objects.get(identifier=ident)
 | 
				
			||||||
 | 
					        self.assertEqual(token.user, self.user)
 | 
				
			||||||
 | 
					        self.assertEqual(token.intent, TokenIntents.INTENT_API)
 | 
				
			||||||
 | 
					        self.assertEqual(token.expiring, True)
 | 
				
			||||||
 | 
					        self.assertTrue(self.user.has_perm("authentik_core.view_token_key", token))
 | 
				
			||||||
 | 
					        response = self.client.put(
 | 
				
			||||||
 | 
					            reverse("authentik_api:token-detail", kwargs={"identifier": ident}),
 | 
				
			||||||
 | 
					            data={"identifier": "user_token_poc_v3", "intent": "api", "user": self.admin.pk},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 400)
 | 
				
			||||||
 | 
					        token.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(token.user, self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_list(self):
 | 
					    def test_list(self):
 | 
				
			||||||
        """Test Token List (Test normal authentication)"""
 | 
					        """Test Token List (Test normal authentication)"""
 | 
				
			||||||
        Token.objects.all().delete()
 | 
					        Token.objects.all().delete()
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,9 @@ from authentik.core.api.transactional_applications import TransactionalApplicati
 | 
				
			|||||||
from authentik.core.api.users import UserViewSet
 | 
					from authentik.core.api.users import UserViewSet
 | 
				
			||||||
from authentik.core.views import apps
 | 
					from authentik.core.views import apps
 | 
				
			||||||
from authentik.core.views.debug import AccessDeniedView
 | 
					from authentik.core.views.debug import AccessDeniedView
 | 
				
			||||||
from authentik.core.views.interface import FlowInterfaceView, InterfaceView
 | 
					from authentik.core.views.interface import InterfaceView
 | 
				
			||||||
from authentik.core.views.session import EndSessionView
 | 
					from authentik.core.views.session import EndSessionView
 | 
				
			||||||
 | 
					from authentik.flows.views.interface import FlowInterfaceView
 | 
				
			||||||
from authentik.root.asgi_middleware import SessionMiddleware
 | 
					from authentik.root.asgi_middleware import SessionMiddleware
 | 
				
			||||||
from authentik.root.messages.consumer import MessageConsumer
 | 
					from authentik.root.messages.consumer import MessageConsumer
 | 
				
			||||||
from authentik.root.middleware import ChannelsLoggingMiddleware
 | 
					from authentik.root.middleware import ChannelsLoggingMiddleware
 | 
				
			||||||
@ -53,6 +54,8 @@ urlpatterns = [
 | 
				
			|||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
        "if/flow/<slug:flow_slug>/",
 | 
					        "if/flow/<slug:flow_slug>/",
 | 
				
			||||||
 | 
					        # FIXME: move this url to the flows app...also will cause all
 | 
				
			||||||
 | 
					        # of the reverse calls to be adjusted
 | 
				
			||||||
        ensure_csrf_cookie(FlowInterfaceView.as_view()),
 | 
					        ensure_csrf_cookie(FlowInterfaceView.as_view()),
 | 
				
			||||||
        name="if-flow",
 | 
					        name="if-flow",
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@
 | 
				
			|||||||
from json import dumps
 | 
					from json import dumps
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.shortcuts import get_object_or_404
 | 
					 | 
				
			||||||
from django.views.generic.base import TemplateView
 | 
					from django.views.generic.base import TemplateView
 | 
				
			||||||
from rest_framework.request import Request
 | 
					from rest_framework.request import Request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,7 +10,6 @@ from authentik import get_build_hash
 | 
				
			|||||||
from authentik.admin.tasks import LOCAL_VERSION
 | 
					from authentik.admin.tasks import LOCAL_VERSION
 | 
				
			||||||
from authentik.api.v3.config import ConfigView
 | 
					from authentik.api.v3.config import ConfigView
 | 
				
			||||||
from authentik.brands.api import CurrentBrandSerializer
 | 
					from authentik.brands.api import CurrentBrandSerializer
 | 
				
			||||||
from authentik.flows.models import Flow
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InterfaceView(TemplateView):
 | 
					class InterfaceView(TemplateView):
 | 
				
			||||||
@ -25,14 +23,3 @@ class InterfaceView(TemplateView):
 | 
				
			|||||||
        kwargs["build"] = get_build_hash()
 | 
					        kwargs["build"] = get_build_hash()
 | 
				
			||||||
        kwargs["url_kwargs"] = self.kwargs
 | 
					        kwargs["url_kwargs"] = self.kwargs
 | 
				
			||||||
        return super().get_context_data(**kwargs)
 | 
					        return super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FlowInterfaceView(InterfaceView):
 | 
					 | 
				
			||||||
    """Flow interface"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    template_name = "if/flow.html"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
 | 
					 | 
				
			||||||
        kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
 | 
					 | 
				
			||||||
        kwargs["inspector"] = "inspector" in self.request.GET
 | 
					 | 
				
			||||||
        return super().get_context_data(**kwargs)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -75,7 +75,10 @@ def on_login_failed(
 | 
				
			|||||||
    **kwargs,
 | 
					    **kwargs,
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    """Failed Login, authentik custom event"""
 | 
					    """Failed Login, authentik custom event"""
 | 
				
			||||||
    Event.new(EventAction.LOGIN_FAILED, **credentials, stage=stage, **kwargs).from_http(request)
 | 
					    user = User.objects.filter(username=credentials.get("username")).first()
 | 
				
			||||||
 | 
					    Event.new(EventAction.LOGIN_FAILED, **credentials, stage=stage, **kwargs).from_http(
 | 
				
			||||||
 | 
					        request, user
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(invitation_used)
 | 
					@receiver(invitation_used)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										54
									
								
								authentik/flows/templates/if/flow-sfe.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								authentik/flows/templates/if/flow-sfe.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					{% load authentik_core %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					    <head>
 | 
				
			||||||
 | 
					        <meta charset="UTF-8">
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
 | 
				
			||||||
 | 
					        <title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
 | 
				
			||||||
 | 
					        <link rel="icon" href="{{ brand.branding_favicon }}">
 | 
				
			||||||
 | 
					        <link rel="shortcut icon" href="{{ brand.branding_favicon }}">
 | 
				
			||||||
 | 
					        {% block head_before %}
 | 
				
			||||||
 | 
					        {% endblock %}
 | 
				
			||||||
 | 
					        <link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
 | 
				
			||||||
 | 
					        <meta name="sentry-trace" content="{{ sentry_trace }}" />
 | 
				
			||||||
 | 
					        {% include "base/header_js.html" %}
 | 
				
			||||||
 | 
					        <style>
 | 
				
			||||||
 | 
					          html,
 | 
				
			||||||
 | 
					          body {
 | 
				
			||||||
 | 
					            height: 100%;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          body {
 | 
				
			||||||
 | 
					            background-image: url("{{ flow.background_url }}");
 | 
				
			||||||
 | 
					            background-repeat: no-repeat;
 | 
				
			||||||
 | 
					            background-size: cover;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          .card {
 | 
				
			||||||
 | 
					            padding: 3rem;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .form-signin {
 | 
				
			||||||
 | 
					            max-width: 330px;
 | 
				
			||||||
 | 
					            padding: 1rem;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .form-signin .form-floating:focus-within {
 | 
				
			||||||
 | 
					            z-index: 2;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          .brand-icon {
 | 
				
			||||||
 | 
					            max-width: 100%;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        </style>
 | 
				
			||||||
 | 
					    </head>
 | 
				
			||||||
 | 
					    <body class="d-flex align-items-center py-4 bg-body-tertiary">
 | 
				
			||||||
 | 
					      <div class="card m-auto">
 | 
				
			||||||
 | 
					        <main class="form-signin w-100 m-auto" id="flow-sfe-container">
 | 
				
			||||||
 | 
					        </main>
 | 
				
			||||||
 | 
					        <span class="mt-3 mb-0 text-muted text-center">{% trans 'Powered by authentik' %}</span>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <script src="{% static 'dist/sfe/index.js' %}"></script>
 | 
				
			||||||
 | 
					    </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										41
									
								
								authentik/flows/views/interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								authentik/flows/views/interface.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					"""Interface views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.shortcuts import get_object_or_404
 | 
				
			||||||
 | 
					from ua_parser.user_agent_parser import Parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.views.interface import InterfaceView
 | 
				
			||||||
 | 
					from authentik.flows.models import Flow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FlowInterfaceView(InterfaceView):
 | 
				
			||||||
 | 
					    """Flow interface"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
 | 
				
			||||||
 | 
					        kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
 | 
				
			||||||
 | 
					        kwargs["inspector"] = "inspector" in self.request.GET
 | 
				
			||||||
 | 
					        return super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def compat_needs_sfe(self) -> bool:
 | 
				
			||||||
 | 
					        """Check if we need to use the simplified flow executor for compatibility"""
 | 
				
			||||||
 | 
					        ua = Parse(self.request.META.get("HTTP_USER_AGENT", ""))
 | 
				
			||||||
 | 
					        if ua["user_agent"]["family"] == "IE":
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        # Only use SFE for Edge 18 and older, after Edge 18 MS switched to chromium which supports
 | 
				
			||||||
 | 
					        # the default flow executor
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            ua["user_agent"]["family"] == "Edge"
 | 
				
			||||||
 | 
					            and int(ua["user_agent"]["major"]) <= 18  # noqa: PLR2004
 | 
				
			||||||
 | 
					        ):  # noqa: PLR2004
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        # https://github.com/AzureAD/microsoft-authentication-library-for-objc
 | 
				
			||||||
 | 
					        # Used by Microsoft Teams/Office on macOS, and also uses a very outdated browser engine
 | 
				
			||||||
 | 
					        if "PKeyAuth" in ua["string"]:
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_template_names(self) -> list[str]:
 | 
				
			||||||
 | 
					        if self.compat_needs_sfe() or "sfe" in self.request.GET:
 | 
				
			||||||
 | 
					            return ["if/flow-sfe.html"]
 | 
				
			||||||
 | 
					        return ["if/flow.html"]
 | 
				
			||||||
@ -19,6 +19,7 @@ from structlog.stdlib import get_logger
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import User
 | 
					from authentik.core.models import User
 | 
				
			||||||
from authentik.events.models import Event
 | 
					from authentik.events.models import Event
 | 
				
			||||||
 | 
					from authentik.lib.expression.exceptions import ControlFlowException
 | 
				
			||||||
from authentik.lib.utils.http import get_http_session
 | 
					from authentik.lib.utils.http import get_http_session
 | 
				
			||||||
from authentik.policies.models import Policy, PolicyBinding
 | 
					from authentik.policies.models import Policy, PolicyBinding
 | 
				
			||||||
from authentik.policies.process import PolicyProcess
 | 
					from authentik.policies.process import PolicyProcess
 | 
				
			||||||
@ -216,7 +217,8 @@ class BaseEvaluator:
 | 
				
			|||||||
                # so the user only sees information relevant to them
 | 
					                # so the user only sees information relevant to them
 | 
				
			||||||
                # and none of our surrounding error handling
 | 
					                # and none of our surrounding error handling
 | 
				
			||||||
                exc.__traceback__ = exc.__traceback__.tb_next
 | 
					                exc.__traceback__ = exc.__traceback__.tb_next
 | 
				
			||||||
                self.handle_error(exc, expression_source)
 | 
					                if not isinstance(exc, ControlFlowException):
 | 
				
			||||||
 | 
					                    self.handle_error(exc, expression_source)
 | 
				
			||||||
                raise exc
 | 
					                raise exc
 | 
				
			||||||
            return result
 | 
					            return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								authentik/lib/expression/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								authentik/lib/expression/exceptions.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					from authentik.lib.sentry import SentryIgnoredException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ControlFlowException(SentryIgnoredException):
 | 
				
			||||||
 | 
					    """Exceptions used to control the flow from exceptions, not reported as a warning/
 | 
				
			||||||
 | 
					    error in logs"""
 | 
				
			||||||
@ -62,7 +62,7 @@ class DomainlessURLValidator(URLValidator):
 | 
				
			|||||||
            r"^(?:[a-z0-9.+-]*)://"  # scheme is validated separately
 | 
					            r"^(?:[a-z0-9.+-]*)://"  # scheme is validated separately
 | 
				
			||||||
            r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?"  # user:pass authentication
 | 
					            r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?"  # user:pass authentication
 | 
				
			||||||
            r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
 | 
					            r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
 | 
				
			||||||
            r"(?::\d{2,5})?"  # port
 | 
					            r"(?::\d{1,5})?"  # port
 | 
				
			||||||
            r"(?:[/?#][^\s]*)?"  # resource path
 | 
					            r"(?:[/?#][^\s]*)?"  # resource path
 | 
				
			||||||
            r"\Z",
 | 
					            r"\Z",
 | 
				
			||||||
            re.IGNORECASE,
 | 
					            re.IGNORECASE,
 | 
				
			||||||
@ -88,7 +88,7 @@ class DomainlessFormattedURLValidator(DomainlessURLValidator):
 | 
				
			|||||||
            r"^(?:[a-z0-9.+-]*)://"  # scheme is validated separately
 | 
					            r"^(?:[a-z0-9.+-]*)://"  # scheme is validated separately
 | 
				
			||||||
            r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?"  # user:pass authentication
 | 
					            r"(?:[^\s:@/]+(?::[^\s:@/]*)?@)?"  # user:pass authentication
 | 
				
			||||||
            r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
 | 
					            r"(?:" + self.ipv4_re + "|" + self.ipv6_re + "|" + self.host_re + ")"
 | 
				
			||||||
            r"(?::\d{2,5})?"  # port
 | 
					            r"(?::\d{1,5})?"  # port
 | 
				
			||||||
            r"(?:[/?#][^\s]*)?"  # resource path
 | 
					            r"(?:[/?#][^\s]*)?"  # resource path
 | 
				
			||||||
            r"\Z",
 | 
					            r"\Z",
 | 
				
			||||||
            re.IGNORECASE,
 | 
					            re.IGNORECASE,
 | 
				
			||||||
 | 
				
			|||||||
@ -59,8 +59,9 @@ def sentry_init(**sentry_init_kwargs):
 | 
				
			|||||||
        "_experiments": {
 | 
					        "_experiments": {
 | 
				
			||||||
            "profiles_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.1)),
 | 
					            "profiles_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.1)),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        **sentry_init_kwargs,
 | 
				
			||||||
 | 
					        **CONFIG.get_dict_from_b64_json("error_reporting.extra_args", {}),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    kwargs.update(**sentry_init_kwargs)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sentry_sdk_init(
 | 
					    sentry_sdk_init(
 | 
				
			||||||
        dsn=CONFIG.get("error_reporting.sentry_dsn"),
 | 
					        dsn=CONFIG.get("error_reporting.sentry_dsn"),
 | 
				
			||||||
 | 
				
			|||||||
@ -4,8 +4,11 @@ from django.db.models import QuerySet
 | 
				
			|||||||
from django.http import HttpRequest
 | 
					from django.http import HttpRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.expression.evaluator import PropertyMappingEvaluator
 | 
					from authentik.core.expression.evaluator import PropertyMappingEvaluator
 | 
				
			||||||
from authentik.core.expression.exceptions import PropertyMappingExpressionException
 | 
					from authentik.core.expression.exceptions import (
 | 
				
			||||||
 | 
					    PropertyMappingExpressionException,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from authentik.core.models import PropertyMapping, User
 | 
					from authentik.core.models import PropertyMapping, User
 | 
				
			||||||
 | 
					from authentik.lib.expression.exceptions import ControlFlowException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PropertyMappingManager:
 | 
					class PropertyMappingManager:
 | 
				
			||||||
@ -57,7 +60,7 @@ class PropertyMappingManager:
 | 
				
			|||||||
            mapping.set_context(user, request, **kwargs)
 | 
					            mapping.set_context(user, request, **kwargs)
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                value = mapping.evaluate(mapping.model.expression)
 | 
					                value = mapping.evaluate(mapping.model.expression)
 | 
				
			||||||
            except PropertyMappingExpressionException as exc:
 | 
					            except (PropertyMappingExpressionException, ControlFlowException) as exc:
 | 
				
			||||||
                raise exc from exc
 | 
					                raise exc from exc
 | 
				
			||||||
            except Exception as exc:
 | 
					            except Exception as exc:
 | 
				
			||||||
                raise PropertyMappingExpressionException(exc, mapping.model) from exc
 | 
					                raise PropertyMappingExpressionException(exc, mapping.model) from exc
 | 
				
			||||||
 | 
				
			|||||||
@ -9,9 +9,9 @@ from structlog.stdlib import get_logger
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from authentik.core.expression.exceptions import (
 | 
					from authentik.core.expression.exceptions import (
 | 
				
			||||||
    PropertyMappingExpressionException,
 | 
					    PropertyMappingExpressionException,
 | 
				
			||||||
    SkipObjectException,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from authentik.events.models import Event, EventAction
 | 
					from authentik.events.models import Event, EventAction
 | 
				
			||||||
 | 
					from authentik.lib.expression.exceptions import ControlFlowException
 | 
				
			||||||
from authentik.lib.sync.mapper import PropertyMappingManager
 | 
					from authentik.lib.sync.mapper import PropertyMappingManager
 | 
				
			||||||
from authentik.lib.sync.outgoing.exceptions import NotFoundSyncException, StopSync
 | 
					from authentik.lib.sync.outgoing.exceptions import NotFoundSyncException, StopSync
 | 
				
			||||||
from authentik.lib.utils.errors import exception_to_string
 | 
					from authentik.lib.utils.errors import exception_to_string
 | 
				
			||||||
@ -92,7 +92,7 @@ class BaseOutgoingSyncClient[
 | 
				
			|||||||
            eval_kwargs.setdefault("user", None)
 | 
					            eval_kwargs.setdefault("user", None)
 | 
				
			||||||
            for value in self.mapper.iter_eval(**eval_kwargs):
 | 
					            for value in self.mapper.iter_eval(**eval_kwargs):
 | 
				
			||||||
                always_merger.merge(raw_final_object, value)
 | 
					                always_merger.merge(raw_final_object, value)
 | 
				
			||||||
        except SkipObjectException as exc:
 | 
					        except ControlFlowException as exc:
 | 
				
			||||||
            raise exc from exc
 | 
					            raise exc from exc
 | 
				
			||||||
        except PropertyMappingExpressionException as exc:
 | 
					        except PropertyMappingExpressionException as exc:
 | 
				
			||||||
            # Value error can be raised when assigning invalid data to an attribute
 | 
					            # Value error can be raised when assigning invalid data to an attribute
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ from authentik.core.api.utils import JSONDictField, ModelSerializer, PassiveSeri
 | 
				
			|||||||
from authentik.core.models import Provider
 | 
					from authentik.core.models import Provider
 | 
				
			||||||
from authentik.enterprise.license import LicenseKey
 | 
					from authentik.enterprise.license import LicenseKey
 | 
				
			||||||
from authentik.enterprise.providers.rac.models import RACProvider
 | 
					from authentik.enterprise.providers.rac.models import RACProvider
 | 
				
			||||||
 | 
					from authentik.lib.utils.time import timedelta_from_string, timedelta_string_validator
 | 
				
			||||||
from authentik.outposts.api.service_connections import ServiceConnectionSerializer
 | 
					from authentik.outposts.api.service_connections import ServiceConnectionSerializer
 | 
				
			||||||
from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
 | 
					from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
 | 
				
			||||||
from authentik.outposts.models import (
 | 
					from authentik.outposts.models import (
 | 
				
			||||||
@ -49,6 +50,10 @@ class OutpostSerializer(ModelSerializer):
 | 
				
			|||||||
    service_connection_obj = ServiceConnectionSerializer(
 | 
					    service_connection_obj = ServiceConnectionSerializer(
 | 
				
			||||||
        source="service_connection", read_only=True
 | 
					        source="service_connection", read_only=True
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    refresh_interval_s = SerializerMethodField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_refresh_interval_s(self, obj: Outpost) -> int:
 | 
				
			||||||
 | 
					        return int(timedelta_from_string(obj.config.refresh_interval).total_seconds())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def validate_name(self, name: str) -> str:
 | 
					    def validate_name(self, name: str) -> str:
 | 
				
			||||||
        """Validate name (especially for embedded outpost)"""
 | 
					        """Validate name (especially for embedded outpost)"""
 | 
				
			||||||
@ -84,7 +89,8 @@ class OutpostSerializer(ModelSerializer):
 | 
				
			|||||||
    def validate_config(self, config) -> dict:
 | 
					    def validate_config(self, config) -> dict:
 | 
				
			||||||
        """Check that the config has all required fields"""
 | 
					        """Check that the config has all required fields"""
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from_dict(OutpostConfig, config)
 | 
					            parsed = from_dict(OutpostConfig, config)
 | 
				
			||||||
 | 
					            timedelta_string_validator(parsed.refresh_interval)
 | 
				
			||||||
        except DaciteError as exc:
 | 
					        except DaciteError as exc:
 | 
				
			||||||
            raise ValidationError(f"Failed to validate config: {str(exc)}") from exc
 | 
					            raise ValidationError(f"Failed to validate config: {str(exc)}") from exc
 | 
				
			||||||
        return config
 | 
					        return config
 | 
				
			||||||
@ -99,6 +105,7 @@ class OutpostSerializer(ModelSerializer):
 | 
				
			|||||||
            "providers_obj",
 | 
					            "providers_obj",
 | 
				
			||||||
            "service_connection",
 | 
					            "service_connection",
 | 
				
			||||||
            "service_connection_obj",
 | 
					            "service_connection_obj",
 | 
				
			||||||
 | 
					            "refresh_interval_s",
 | 
				
			||||||
            "token_identifier",
 | 
					            "token_identifier",
 | 
				
			||||||
            "config",
 | 
					            "config",
 | 
				
			||||||
            "managed",
 | 
					            "managed",
 | 
				
			||||||
 | 
				
			|||||||
@ -61,6 +61,7 @@ class OutpostConfig:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    log_level: str = CONFIG.get("log_level")
 | 
					    log_level: str = CONFIG.get("log_level")
 | 
				
			||||||
    object_naming_template: str = field(default="ak-outpost-%(name)s")
 | 
					    object_naming_template: str = field(default="ak-outpost-%(name)s")
 | 
				
			||||||
 | 
					    refresh_interval: str = "minutes=5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    container_image: str | None = field(default=None)
 | 
					    container_image: str | None = field(default=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
"""Reputation policy API Views"""
 | 
					"""Reputation policy API Views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					from django_filters.filters import BaseInFilter, CharFilter
 | 
				
			||||||
 | 
					from django_filters.filterset import FilterSet
 | 
				
			||||||
from rest_framework import mixins
 | 
					from rest_framework import mixins
 | 
				
			||||||
from rest_framework.exceptions import ValidationError
 | 
					from rest_framework.exceptions import ValidationError
 | 
				
			||||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
 | 
					from rest_framework.viewsets import GenericViewSet, ModelViewSet
 | 
				
			||||||
@ -11,6 +13,10 @@ from authentik.policies.api.policies import PolicySerializer
 | 
				
			|||||||
from authentik.policies.reputation.models import Reputation, ReputationPolicy
 | 
					from authentik.policies.reputation.models import Reputation, ReputationPolicy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CharInFilter(BaseInFilter, CharFilter):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReputationPolicySerializer(PolicySerializer):
 | 
					class ReputationPolicySerializer(PolicySerializer):
 | 
				
			||||||
    """Reputation Policy Serializer"""
 | 
					    """Reputation Policy Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,6 +44,16 @@ class ReputationPolicyViewSet(UsedByMixin, ModelViewSet):
 | 
				
			|||||||
    ordering = ["name"]
 | 
					    ordering = ["name"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReputationFilter(FilterSet):
 | 
				
			||||||
 | 
					    """Filter for reputation"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    identifier_in = CharInFilter(field_name="identifier", lookup_expr="in")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Reputation
 | 
				
			||||||
 | 
					        fields = ["identifier", "ip", "score"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReputationSerializer(ModelSerializer):
 | 
					class ReputationSerializer(ModelSerializer):
 | 
				
			||||||
    """Reputation Serializer"""
 | 
					    """Reputation Serializer"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,5 +82,5 @@ class ReputationViewSet(
 | 
				
			|||||||
    queryset = Reputation.objects.all()
 | 
					    queryset = Reputation.objects.all()
 | 
				
			||||||
    serializer_class = ReputationSerializer
 | 
					    serializer_class = ReputationSerializer
 | 
				
			||||||
    search_fields = ["identifier", "ip", "score"]
 | 
					    search_fields = ["identifier", "ip", "score"]
 | 
				
			||||||
    filterset_fields = ["identifier", "ip", "score"]
 | 
					    filterset_class = ReputationFilter
 | 
				
			||||||
    ordering = ["ip"]
 | 
					    ordering = ["ip"]
 | 
				
			||||||
 | 
				
			|||||||
@ -4,9 +4,10 @@ from urllib.parse import urlencode
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import Application
 | 
					from authentik.core.models import Application, Group
 | 
				
			||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_brand, create_test_flow
 | 
					from authentik.core.tests.utils import create_test_admin_user, create_test_brand, create_test_flow
 | 
				
			||||||
from authentik.lib.generators import generate_id
 | 
					from authentik.lib.generators import generate_id
 | 
				
			||||||
 | 
					from authentik.policies.models import PolicyBinding
 | 
				
			||||||
from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
 | 
					from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
 | 
				
			||||||
from authentik.providers.oauth2.tests.utils import OAuthTestCase
 | 
					from authentik.providers.oauth2.tests.utils import OAuthTestCase
 | 
				
			||||||
from authentik.providers.oauth2.views.device_init import QS_KEY_CODE
 | 
					from authentik.providers.oauth2.views.device_init import QS_KEY_CODE
 | 
				
			||||||
@ -77,3 +78,23 @@ class TesOAuth2DeviceInit(OAuthTestCase):
 | 
				
			|||||||
            + "?"
 | 
					            + "?"
 | 
				
			||||||
            + urlencode({QS_KEY_CODE: token.user_code}),
 | 
					            + urlencode({QS_KEY_CODE: token.user_code}),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_device_init_denied(self):
 | 
				
			||||||
 | 
					        """Test device init"""
 | 
				
			||||||
 | 
					        group = Group.objects.create(name="foo")
 | 
				
			||||||
 | 
					        PolicyBinding.objects.create(
 | 
				
			||||||
 | 
					            group=group,
 | 
				
			||||||
 | 
					            target=self.application,
 | 
				
			||||||
 | 
					            order=0,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        token = DeviceToken.objects.create(
 | 
				
			||||||
 | 
					            user_code="foo",
 | 
				
			||||||
 | 
					            provider=self.provider,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        res = self.client.get(
 | 
				
			||||||
 | 
					            reverse("authentik_providers_oauth2_root:device-login")
 | 
				
			||||||
 | 
					            + "?"
 | 
				
			||||||
 | 
					            + urlencode({QS_KEY_CODE: token.user_code})
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.assertEqual(res.status_code, 200)
 | 
				
			||||||
 | 
					        self.assertIn(b"Permission denied", res.content)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,10 +11,11 @@ from django.views.decorators.csrf import csrf_exempt
 | 
				
			|||||||
from rest_framework.throttling import AnonRateThrottle
 | 
					from rest_framework.throttling import AnonRateThrottle
 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.core.models import Application
 | 
				
			||||||
from authentik.lib.config import CONFIG
 | 
					from authentik.lib.config import CONFIG
 | 
				
			||||||
from authentik.lib.utils.time import timedelta_from_string
 | 
					from authentik.lib.utils.time import timedelta_from_string
 | 
				
			||||||
from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
 | 
					from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
 | 
				
			||||||
from authentik.providers.oauth2.views.device_init import QS_KEY_CODE, get_application
 | 
					from authentik.providers.oauth2.views.device_init import QS_KEY_CODE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LOGGER = get_logger()
 | 
					LOGGER = get_logger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -37,7 +38,9 @@ class DeviceView(View):
 | 
				
			|||||||
        ).first()
 | 
					        ).first()
 | 
				
			||||||
        if not provider:
 | 
					        if not provider:
 | 
				
			||||||
            return HttpResponseBadRequest()
 | 
					            return HttpResponseBadRequest()
 | 
				
			||||||
        if not get_application(provider):
 | 
					        try:
 | 
				
			||||||
 | 
					            _ = provider.application
 | 
				
			||||||
 | 
					        except Application.DoesNotExist:
 | 
				
			||||||
            return HttpResponseBadRequest()
 | 
					            return HttpResponseBadRequest()
 | 
				
			||||||
        self.provider = provider
 | 
					        self.provider = provider
 | 
				
			||||||
        self.client_id = client_id
 | 
					        self.client_id = client_id
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
"""Device flow views"""
 | 
					"""Device flow views"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.http import HttpRequest, HttpResponse
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from django.views import View
 | 
					 | 
				
			||||||
from rest_framework.exceptions import ValidationError
 | 
					from rest_framework.exceptions import ValidationError
 | 
				
			||||||
from rest_framework.fields import CharField, IntegerField
 | 
					from rest_framework.fields import CharField, IntegerField
 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
@ -16,7 +17,8 @@ from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO,
 | 
				
			|||||||
from authentik.flows.stage import ChallengeStageView
 | 
					from authentik.flows.stage import ChallengeStageView
 | 
				
			||||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
 | 
					from authentik.flows.views.executor import SESSION_KEY_PLAN
 | 
				
			||||||
from authentik.lib.utils.urls import redirect_with_qs
 | 
					from authentik.lib.utils.urls import redirect_with_qs
 | 
				
			||||||
from authentik.providers.oauth2.models import DeviceToken, OAuth2Provider
 | 
					from authentik.policies.views import PolicyAccessView
 | 
				
			||||||
 | 
					from authentik.providers.oauth2.models import DeviceToken
 | 
				
			||||||
from authentik.providers.oauth2.views.device_finish import (
 | 
					from authentik.providers.oauth2.views.device_finish import (
 | 
				
			||||||
    PLAN_CONTEXT_DEVICE,
 | 
					    PLAN_CONTEXT_DEVICE,
 | 
				
			||||||
    OAuthDeviceCodeFinishStage,
 | 
					    OAuthDeviceCodeFinishStage,
 | 
				
			||||||
@ -31,60 +33,52 @@ LOGGER = get_logger()
 | 
				
			|||||||
QS_KEY_CODE = "code"  # nosec
 | 
					QS_KEY_CODE = "code"  # nosec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_application(provider: OAuth2Provider) -> Application | None:
 | 
					class CodeValidatorView(PolicyAccessView):
 | 
				
			||||||
    """Get application from provider"""
 | 
					    """Helper to validate frontside token"""
 | 
				
			||||||
    try:
 | 
					
 | 
				
			||||||
        app = provider.application
 | 
					    def __init__(self, code: str, **kwargs: Any) -> None:
 | 
				
			||||||
        if not app:
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
 | 
					        self.code = code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def resolve_provider_application(self):
 | 
				
			||||||
 | 
					        self.token = DeviceToken.objects.filter(user_code=self.code).first()
 | 
				
			||||||
 | 
					        if not self.token:
 | 
				
			||||||
 | 
					            raise Application.DoesNotExist
 | 
				
			||||||
 | 
					        self.provider = self.token.provider
 | 
				
			||||||
 | 
					        self.application = self.token.provider.application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request: HttpRequest, *args, **kwargs):
 | 
				
			||||||
 | 
					        scope_descriptions = UserInfoView().get_scope_descriptions(self.token.scope, self.provider)
 | 
				
			||||||
 | 
					        planner = FlowPlanner(self.provider.authorization_flow)
 | 
				
			||||||
 | 
					        planner.allow_empty_flows = True
 | 
				
			||||||
 | 
					        planner.use_cache = False
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            plan = planner.plan(
 | 
				
			||||||
 | 
					                request,
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_SSO: True,
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_APPLICATION: self.application,
 | 
				
			||||||
 | 
					                    # OAuth2 related params
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_DEVICE: self.token,
 | 
				
			||||||
 | 
					                    # Consent related params
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_CONSENT_HEADER: _("You're about to sign into %(application)s.")
 | 
				
			||||||
 | 
					                    % {"application": self.application.name},
 | 
				
			||||||
 | 
					                    PLAN_CONTEXT_CONSENT_PERMISSIONS: scope_descriptions,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        except FlowNonApplicableException:
 | 
				
			||||||
 | 
					            LOGGER.warning("Flow not applicable to user")
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
        return app
 | 
					        plan.insert_stage(in_memory_stage(OAuthDeviceCodeFinishStage))
 | 
				
			||||||
    except Application.DoesNotExist:
 | 
					        request.session[SESSION_KEY_PLAN] = plan
 | 
				
			||||||
        return None
 | 
					        return redirect_with_qs(
 | 
				
			||||||
 | 
					            "authentik_core:if-flow",
 | 
				
			||||||
 | 
					            request.GET,
 | 
				
			||||||
def validate_code(code: int, request: HttpRequest) -> HttpResponse | None:
 | 
					            flow_slug=self.token.provider.authorization_flow.slug,
 | 
				
			||||||
    """Validate user token"""
 | 
					 | 
				
			||||||
    token = DeviceToken.objects.filter(
 | 
					 | 
				
			||||||
        user_code=code,
 | 
					 | 
				
			||||||
    ).first()
 | 
					 | 
				
			||||||
    if not token:
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    app = get_application(token.provider)
 | 
					 | 
				
			||||||
    if not app:
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    scope_descriptions = UserInfoView().get_scope_descriptions(token.scope, token.provider)
 | 
					 | 
				
			||||||
    planner = FlowPlanner(token.provider.authorization_flow)
 | 
					 | 
				
			||||||
    planner.allow_empty_flows = True
 | 
					 | 
				
			||||||
    planner.use_cache = False
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        plan = planner.plan(
 | 
					 | 
				
			||||||
            request,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_SSO: True,
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_APPLICATION: app,
 | 
					 | 
				
			||||||
                # OAuth2 related params
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_DEVICE: token,
 | 
					 | 
				
			||||||
                # Consent related params
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_CONSENT_HEADER: _("You're about to sign into %(application)s.")
 | 
					 | 
				
			||||||
                % {"application": app.name},
 | 
					 | 
				
			||||||
                PLAN_CONTEXT_CONSENT_PERMISSIONS: scope_descriptions,
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    except FlowNonApplicableException:
 | 
					 | 
				
			||||||
        LOGGER.warning("Flow not applicable to user")
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
    plan.insert_stage(in_memory_stage(OAuthDeviceCodeFinishStage))
 | 
					 | 
				
			||||||
    request.session[SESSION_KEY_PLAN] = plan
 | 
					 | 
				
			||||||
    return redirect_with_qs(
 | 
					 | 
				
			||||||
        "authentik_core:if-flow",
 | 
					 | 
				
			||||||
        request.GET,
 | 
					 | 
				
			||||||
        flow_slug=token.provider.authorization_flow.slug,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeviceEntryView(View):
 | 
					class DeviceEntryView(PolicyAccessView):
 | 
				
			||||||
    """View used to initiate the device-code flow, url entered by endusers"""
 | 
					    """View used to initiate the device-code flow, url entered by endusers"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dispatch(self, request: HttpRequest) -> HttpResponse:
 | 
					    def dispatch(self, request: HttpRequest) -> HttpResponse:
 | 
				
			||||||
@ -94,7 +88,9 @@ class DeviceEntryView(View):
 | 
				
			|||||||
            LOGGER.info("Brand has no device code flow configured", brand=brand)
 | 
					            LOGGER.info("Brand has no device code flow configured", brand=brand)
 | 
				
			||||||
            return HttpResponse(status=404)
 | 
					            return HttpResponse(status=404)
 | 
				
			||||||
        if QS_KEY_CODE in request.GET:
 | 
					        if QS_KEY_CODE in request.GET:
 | 
				
			||||||
            validation = validate_code(request.GET[QS_KEY_CODE], request)
 | 
					            validation = CodeValidatorView(request.GET[QS_KEY_CODE], request=request).dispatch(
 | 
				
			||||||
 | 
					                request
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            if validation:
 | 
					            if validation:
 | 
				
			||||||
                return validation
 | 
					                return validation
 | 
				
			||||||
            LOGGER.info("Got code from query parameter but no matching token found")
 | 
					            LOGGER.info("Got code from query parameter but no matching token found")
 | 
				
			||||||
@ -131,7 +127,7 @@ class OAuthDeviceCodeChallengeResponse(ChallengeResponse):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def validate_code(self, code: int) -> HttpResponse | None:
 | 
					    def validate_code(self, code: int) -> HttpResponse | None:
 | 
				
			||||||
        """Validate code and save the returned http response"""
 | 
					        """Validate code and save the returned http response"""
 | 
				
			||||||
        response = validate_code(code, self.stage.request)
 | 
					        response = CodeValidatorView(code, request=self.stage.request).dispatch(self.stage.request)
 | 
				
			||||||
        if not response:
 | 
					        if not response:
 | 
				
			||||||
            raise ValidationError(_("Invalid code"), "invalid")
 | 
					            raise ValidationError(_("Invalid code"), "invalid")
 | 
				
			||||||
        return response
 | 
					        return response
 | 
				
			||||||
 | 
				
			|||||||
@ -268,7 +268,7 @@ class SAMLProviderViewSet(UsedByMixin, ModelViewSet):
 | 
				
			|||||||
        except ValueError as exc:  # pragma: no cover
 | 
					        except ValueError as exc:  # pragma: no cover
 | 
				
			||||||
            LOGGER.warning(str(exc))
 | 
					            LOGGER.warning(str(exc))
 | 
				
			||||||
            raise ValidationError(
 | 
					            raise ValidationError(
 | 
				
			||||||
                _("Failed to import Metadata: {messages}".format_map({"message": str(exc)})),
 | 
					                _("Failed to import Metadata: {messages}".format_map({"messages": str(exc)})),
 | 
				
			||||||
            ) from None
 | 
					            ) from None
 | 
				
			||||||
        return Response(status=204)
 | 
					        return Response(status=204)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -89,6 +89,6 @@ class SCIMClient[TModel: "Model", TConnection: "Model", TSchema: "BaseModel"](
 | 
				
			|||||||
            return ServiceProviderConfiguration.model_validate(
 | 
					            return ServiceProviderConfiguration.model_validate(
 | 
				
			||||||
                self._request("GET", "/ServiceProviderConfig")
 | 
					                self._request("GET", "/ServiceProviderConfig")
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        except (ValidationError, SCIMRequestException) as exc:
 | 
					        except (ValidationError, SCIMRequestException, NotFoundSyncException) as exc:
 | 
				
			||||||
            self.logger.warning("failed to get ServiceProviderConfig", exc=exc)
 | 
					            self.logger.warning("failed to get ServiceProviderConfig", exc=exc)
 | 
				
			||||||
            return default_config
 | 
					            return default_config
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ from collections import OrderedDict
 | 
				
			|||||||
from hashlib import sha512
 | 
					from hashlib import sha512
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import orjson
 | 
				
			||||||
from celery.schedules import crontab
 | 
					from celery.schedules import crontab
 | 
				
			||||||
from django.conf import ImproperlyConfigured
 | 
					from django.conf import ImproperlyConfigured
 | 
				
			||||||
from sentry_sdk import set_tag
 | 
					from sentry_sdk import set_tag
 | 
				
			||||||
@ -178,13 +179,17 @@ REST_FRAMEWORK = {
 | 
				
			|||||||
        "rest_framework.filters.OrderingFilter",
 | 
					        "rest_framework.filters.OrderingFilter",
 | 
				
			||||||
        "rest_framework.filters.SearchFilter",
 | 
					        "rest_framework.filters.SearchFilter",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "DEFAULT_PARSER_CLASSES": [
 | 
					 | 
				
			||||||
        "rest_framework.parsers.JSONParser",
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
    "DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",),
 | 
					    "DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",),
 | 
				
			||||||
    "DEFAULT_AUTHENTICATION_CLASSES": ("authentik.api.authentication.TokenAuthentication",),
 | 
					    "DEFAULT_AUTHENTICATION_CLASSES": ("authentik.api.authentication.TokenAuthentication",),
 | 
				
			||||||
    "DEFAULT_RENDERER_CLASSES": [
 | 
					    "DEFAULT_RENDERER_CLASSES": [
 | 
				
			||||||
        "rest_framework.renderers.JSONRenderer",
 | 
					        "drf_orjson_renderer.renderers.ORJSONRenderer",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "ORJSON_RENDERER_OPTIONS": [
 | 
				
			||||||
 | 
					        orjson.OPT_NON_STR_KEYS,
 | 
				
			||||||
 | 
					        orjson.OPT_UTC_Z,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "DEFAULT_PARSER_CLASSES": [
 | 
				
			||||||
 | 
					        "drf_orjson_renderer.parsers.ORJSONParser",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
 | 
					    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
 | 
				
			||||||
    "TEST_REQUEST_DEFAULT_FORMAT": "json",
 | 
					    "TEST_REQUEST_DEFAULT_FORMAT": "json",
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,7 @@ from django.core.cache import cache
 | 
				
			|||||||
from django.core.exceptions import SuspiciousOperation
 | 
					from django.core.exceptions import SuspiciousOperation
 | 
				
			||||||
from django.http import HttpRequest
 | 
					from django.http import HttpRequest
 | 
				
			||||||
from django.utils.timezone import now
 | 
					from django.utils.timezone import now
 | 
				
			||||||
 | 
					from lxml import etree  # nosec
 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import (
 | 
					from authentik.core.models import (
 | 
				
			||||||
@ -240,7 +241,7 @@ class ResponseProcessor:
 | 
				
			|||||||
            name_id.text,
 | 
					            name_id.text,
 | 
				
			||||||
            delete_none_values(self.get_attributes()),
 | 
					            delete_none_values(self.get_attributes()),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        flow_manager.policy_context["saml_response"] = self._root
 | 
					        flow_manager.policy_context["saml_response"] = etree.tostring(self._root)
 | 
				
			||||||
        return flow_manager
 | 
					        return flow_manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -325,7 +325,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
 | 
				
			|||||||
            serializer = SelectableStageSerializer(
 | 
					            serializer = SelectableStageSerializer(
 | 
				
			||||||
                data={
 | 
					                data={
 | 
				
			||||||
                    "pk": stage.pk,
 | 
					                    "pk": stage.pk,
 | 
				
			||||||
                    "name": stage.friendly_name or stage.name,
 | 
					                    "name": getattr(stage, "friendly_name", stage.name),
 | 
				
			||||||
                    "verbose_name": str(stage._meta.verbose_name)
 | 
					                    "verbose_name": str(stage._meta.verbose_name)
 | 
				
			||||||
                    .replace("Setup Stage", "")
 | 
					                    .replace("Setup Stage", "")
 | 
				
			||||||
                    .strip(),
 | 
					                    .strip(),
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -120,7 +120,7 @@
 | 
				
			|||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
              <td align="center">
 | 
					              <td align="center">
 | 
				
			||||||
                Powered by <a href="https://goauthentik.io?utm_source=authentik&utm_medium=email">authentik</a>.
 | 
					                Powered by <a rel="noopener noreferrer" target="_blank" href="https://goauthentik.io?utm_source=authentik&utm_medium=email">authentik</a>.
 | 
				
			||||||
              </td>
 | 
					              </td>
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
          </table>
 | 
					          </table>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,9 @@
 | 
				
			|||||||
"""Sessions bound to ASN/Network and GeoIP/Continent/etc"""
 | 
					"""Sessions bound to ASN/Network and GeoIP/Continent/etc"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					 | 
				
			||||||
from django.contrib.auth.middleware import AuthenticationMiddleware
 | 
					from django.contrib.auth.middleware import AuthenticationMiddleware
 | 
				
			||||||
from django.contrib.auth.signals import user_logged_out
 | 
					from django.contrib.auth.signals import user_logged_out
 | 
				
			||||||
 | 
					from django.contrib.auth.views import redirect_to_login
 | 
				
			||||||
from django.http.request import HttpRequest
 | 
					from django.http.request import HttpRequest
 | 
				
			||||||
from django.shortcuts import redirect
 | 
					 | 
				
			||||||
from structlog.stdlib import get_logger
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.core.models import AuthenticatedSession
 | 
					from authentik.core.models import AuthenticatedSession
 | 
				
			||||||
@ -87,7 +86,7 @@ class BoundSessionMiddleware(SessionMiddleware):
 | 
				
			|||||||
            AuthenticationMiddleware(lambda request: request).process_request(request)
 | 
					            AuthenticationMiddleware(lambda request: request).process_request(request)
 | 
				
			||||||
            logout_extra(request, exc)
 | 
					            logout_extra(request, exc)
 | 
				
			||||||
            request.session.clear()
 | 
					            request.session.clear()
 | 
				
			||||||
            return redirect(settings.LOGIN_URL)
 | 
					            return redirect_to_login(request.get_full_path())
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def recheck_session(self, request: HttpRequest):
 | 
					    def recheck_session(self, request: HttpRequest):
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ from django.contrib.auth import update_session_auth_hash
 | 
				
			|||||||
from django.db import transaction
 | 
					from django.db import transaction
 | 
				
			||||||
from django.db.utils import IntegrityError, InternalError
 | 
					from django.db.utils import IntegrityError, InternalError
 | 
				
			||||||
from django.http import HttpRequest, HttpResponse
 | 
					from django.http import HttpRequest, HttpResponse
 | 
				
			||||||
 | 
					from django.utils.functional import SimpleLazyObject
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from rest_framework.exceptions import ValidationError
 | 
					from rest_framework.exceptions import ValidationError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -118,6 +119,14 @@ class UserWriteStageView(StageView):
 | 
				
			|||||||
                UserWriteStageView.write_attribute(user, key, value)
 | 
					                UserWriteStageView.write_attribute(user, key, value)
 | 
				
			||||||
            # User has this key already
 | 
					            # User has this key already
 | 
				
			||||||
            elif hasattr(user, key):
 | 
					            elif hasattr(user, key):
 | 
				
			||||||
 | 
					                if isinstance(user, SimpleLazyObject):
 | 
				
			||||||
 | 
					                    user._setup()
 | 
				
			||||||
 | 
					                    user = user._wrapped
 | 
				
			||||||
 | 
					                attr = getattr(type(user), key)
 | 
				
			||||||
 | 
					                if isinstance(attr, property):
 | 
				
			||||||
 | 
					                    if not attr.fset:
 | 
				
			||||||
 | 
					                        self.logger.info("discarding key", key=key)
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
                setattr(user, key, value)
 | 
					                setattr(user, key, value)
 | 
				
			||||||
            # If none of the cases above matched, we have an attribute that the user doesn't have,
 | 
					            # If none of the cases above matched, we have an attribute that the user doesn't have,
 | 
				
			||||||
            # has no setter for, is not a nested attributes value and as such is invalid
 | 
					            # has no setter for, is not a nested attributes value and as such is invalid
 | 
				
			||||||
 | 
				
			|||||||
@ -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.4.2 Blueprint schema",
 | 
					    "title": "authentik 2024.6.0 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.4.2}
 | 
					    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.0}
 | 
				
			||||||
    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.4.2}
 | 
					    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.0}
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
    command: worker
 | 
					    command: worker
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							@ -23,12 +23,12 @@ require (
 | 
				
			|||||||
	github.com/pires/go-proxyproto v0.7.0
 | 
						github.com/pires/go-proxyproto v0.7.0
 | 
				
			||||||
	github.com/prometheus/client_golang v1.19.1
 | 
						github.com/prometheus/client_golang v1.19.1
 | 
				
			||||||
	github.com/redis/go-redis/v9 v9.5.3
 | 
						github.com/redis/go-redis/v9 v9.5.3
 | 
				
			||||||
	github.com/sethvargo/go-envconfig v1.0.3
 | 
						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.2024042.13
 | 
						goauthentik.io/api/v3 v3.2024060.6
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
 | 
						golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
 | 
				
			||||||
	golang.org/x/oauth2 v0.21.0
 | 
						golang.org/x/oauth2 v0.21.0
 | 
				
			||||||
	golang.org/x/sync v0.7.0
 | 
						golang.org/x/sync v0.7.0
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.sum
									
									
									
									
									
								
							@ -248,8 +248,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 | 
				
			|||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 | 
					github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
 | 
				
			||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 | 
					github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
 | 
				
			||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
					github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 | 
				
			||||||
github.com/sethvargo/go-envconfig v1.0.3 h1:ZDxFGT1M7RPX0wgDOCdZMidrEB+NrayYr6fL0/+pk4I=
 | 
					github.com/sethvargo/go-envconfig v1.1.0 h1:cWZiJxeTm7AlCvzGXrEXaSTCNgip5oJepekh/BOQuog=
 | 
				
			||||||
github.com/sethvargo/go-envconfig v1.0.3/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw=
 | 
					github.com/sethvargo/go-envconfig v1.1.0/go.mod h1:JLd0KFWQYzyENqnEPWWZ49i4vzZo/6nRidxI8YvGiHw=
 | 
				
			||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
					github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
					github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 | 
				
			||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
					github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 | 
				
			||||||
@ -294,8 +294,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.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 | 
					go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 | 
				
			||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 | 
					go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 | 
				
			||||||
goauthentik.io/api/v3 v3.2024042.13 h1:eklVXXLH0tV+02puhxzWJZ8l6HhxmeVMYp/M6sdaji8=
 | 
					goauthentik.io/api/v3 v3.2024060.6 h1:6xN5GXv9G2w6JlqdtSo5p/lBmvBwbNGRTbBwSr1EOKU=
 | 
				
			||||||
goauthentik.io/api/v3 v3.2024042.13/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
 | 
					goauthentik.io/api/v3 v3.2024060.6/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=
 | 
				
			||||||
 | 
				
			|||||||
@ -29,4 +29,4 @@ func UserAgent() string {
 | 
				
			|||||||
	return fmt.Sprintf("authentik@%s", FullVersion())
 | 
						return fmt.Sprintf("authentik@%s", FullVersion())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const VERSION = "2024.4.2"
 | 
					const VERSION = "2024.6.0"
 | 
				
			||||||
 | 
				
			|||||||
@ -183,7 +183,7 @@ func (ac *APIController) startWSHealth() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (ac *APIController) startIntervalUpdater() {
 | 
					func (ac *APIController) startIntervalUpdater() {
 | 
				
			||||||
	logger := ac.logger.WithField("loop", "interval-updater")
 | 
						logger := ac.logger.WithField("loop", "interval-updater")
 | 
				
			||||||
	ticker := time.NewTicker(5 * time.Minute)
 | 
						ticker := time.NewTicker(time.Duration(ac.Outpost.RefreshIntervalS) * time.Second)
 | 
				
			||||||
	for ; true; <-ticker.C {
 | 
						for ; true; <-ticker.C {
 | 
				
			||||||
		logger.Debug("Running interval update")
 | 
							logger.Debug("Running interval update")
 | 
				
			||||||
		err := ac.OnRefresh()
 | 
							err := ac.OnRefresh()
 | 
				
			||||||
@ -198,6 +198,7 @@ func (ac *APIController) startIntervalUpdater() {
 | 
				
			|||||||
				"build":        constants.BUILD("tagged"),
 | 
									"build":        constants.BUILD("tagged"),
 | 
				
			||||||
			}).SetToCurrentTime()
 | 
								}).SetToCurrentTime()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ticker.Reset(time.Duration(ac.Outpost.RefreshIntervalS) * time.Second)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -48,9 +48,9 @@
 | 
				
			|||||||
                <footer class="pf-c-login__footer">
 | 
					                <footer class="pf-c-login__footer">
 | 
				
			||||||
                    <ul class="pf-c-list pf-m-inline">
 | 
					                    <ul class="pf-c-list pf-m-inline">
 | 
				
			||||||
                        <li>
 | 
					                        <li>
 | 
				
			||||||
                            <a href="https://goauthentik.io?utm_source=authentik_outpost&utm_campaign=proxy_error">
 | 
					                            <span>
 | 
				
			||||||
                                Powered by authentik
 | 
					                                Powered by authentik
 | 
				
			||||||
                            </a>
 | 
					                            </span>
 | 
				
			||||||
                        </li>
 | 
					                        </li>
 | 
				
			||||||
                    </ul>
 | 
					                    </ul>
 | 
				
			||||||
                </footer>
 | 
					                </footer>
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -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-06-05 00:07+0000\n"
 | 
					"POT-Creation-Date: 2024-06-16 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: Anton, 2024\n"
 | 
					"Last-Translator: Anton, 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"
 | 
				
			||||||
@ -394,7 +394,7 @@ msgstr "Домой"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#: authentik/core/templates/login/base_full.html
 | 
					#: authentik/core/templates/login/base_full.html
 | 
				
			||||||
msgid "Powered by authentik"
 | 
					msgid "Powered by authentik"
 | 
				
			||||||
msgstr "Поддерживается authentik"
 | 
					msgstr "Основано на authentik"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/core/views/apps.py authentik/providers/oauth2/views/authorize.py
 | 
					#: authentik/core/views/apps.py authentik/providers/oauth2/views/authorize.py
 | 
				
			||||||
#: authentik/providers/oauth2/views/device_init.py
 | 
					#: authentik/providers/oauth2/views/device_init.py
 | 
				
			||||||
@ -463,6 +463,22 @@ msgstr "Для доступа к этой функции требуется Ente
 | 
				
			|||||||
msgid "Feature only accessible for internal users."
 | 
					msgid "Feature only accessible for internal users."
 | 
				
			||||||
msgstr "Функция доступна только для внутренних пользователей."
 | 
					msgstr "Функция доступна только для внутренних пользователей."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/enterprise/providers/google_workspace/models.py
 | 
				
			||||||
 | 
					msgid "Google Workspace Provider User"
 | 
				
			||||||
 | 
					msgstr "Пользователь провайдера Google Workspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/enterprise/providers/google_workspace/models.py
 | 
				
			||||||
 | 
					msgid "Google Workspace Provider Users"
 | 
				
			||||||
 | 
					msgstr "Пользователи провайдера Google Workspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/enterprise/providers/google_workspace/models.py
 | 
				
			||||||
 | 
					msgid "Google Workspace Provider Group"
 | 
				
			||||||
 | 
					msgstr "Группа провайдера Google Workspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: authentik/enterprise/providers/google_workspace/models.py
 | 
				
			||||||
 | 
					msgid "Google Workspace Provider Groups"
 | 
				
			||||||
 | 
					msgstr "Группы провайдера Google Workspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/google_workspace/models.py
 | 
					#: authentik/enterprise/providers/google_workspace/models.py
 | 
				
			||||||
#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
					#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
				
			||||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
 | 
					#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
 | 
				
			||||||
@ -485,21 +501,17 @@ msgstr "Сопоставление провайдера Google Workspace"
 | 
				
			|||||||
msgid "Google Workspace Provider Mappings"
 | 
					msgid "Google Workspace Provider Mappings"
 | 
				
			||||||
msgstr "Сопоставления провайдера Google Workspace"
 | 
					msgstr "Сопоставления провайдера Google Workspace"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/google_workspace/models.py
 | 
					#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
				
			||||||
msgid "Google Workspace Provider User"
 | 
					msgid "Microsoft Entra Provider User"
 | 
				
			||||||
msgstr "Пользователь провайдера Google Workspace"
 | 
					msgstr "Пользователь провайдера Microsoft Entra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/google_workspace/models.py
 | 
					#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
				
			||||||
msgid "Google Workspace Provider Users"
 | 
					msgid "Microsoft Entra Provider Group"
 | 
				
			||||||
msgstr "Пользователи провайдера Google Workspace"
 | 
					msgstr "Группа провайдера Microsoft Entra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/google_workspace/models.py
 | 
					#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
				
			||||||
msgid "Google Workspace Provider Group"
 | 
					msgid "Microsoft Entra Provider Groups"
 | 
				
			||||||
msgstr "Группа провайдера Google Workspace"
 | 
					msgstr "Группы провайдера Microsoft Entra"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/enterprise/providers/google_workspace/models.py
 | 
					 | 
				
			||||||
msgid "Google Workspace Provider Groups"
 | 
					 | 
				
			||||||
msgstr "Группы провайдера Google Workspace"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
					#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
				
			||||||
msgid "Microsoft Entra Provider"
 | 
					msgid "Microsoft Entra Provider"
 | 
				
			||||||
@ -517,18 +529,6 @@ msgstr "Сопоставление провайдера Microsoft Entra"
 | 
				
			|||||||
msgid "Microsoft Entra Provider Mappings"
 | 
					msgid "Microsoft Entra Provider Mappings"
 | 
				
			||||||
msgstr "Сопоставления провайдера Microsoft Entra"
 | 
					msgstr "Сопоставления провайдера Microsoft Entra"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
					 | 
				
			||||||
msgid "Microsoft Entra Provider User"
 | 
					 | 
				
			||||||
msgstr "Пользователь провайдера Microsoft Entra"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
					 | 
				
			||||||
msgid "Microsoft Entra Provider Group"
 | 
					 | 
				
			||||||
msgstr "Группа провайдера Microsoft Entra"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/enterprise/providers/microsoft_entra/models.py
 | 
					 | 
				
			||||||
msgid "Microsoft Entra Provider Groups"
 | 
					 | 
				
			||||||
msgstr "Группы провайдера Microsoft Entra"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#: authentik/enterprise/providers/rac/models.py
 | 
					#: authentik/enterprise/providers/rac/models.py
 | 
				
			||||||
#: authentik/stages/user_login/models.py
 | 
					#: authentik/stages/user_login/models.py
 | 
				
			||||||
msgid ""
 | 
					msgid ""
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "@goauthentik/authentik",
 | 
					    "name": "@goauthentik/authentik",
 | 
				
			||||||
  "version": "2024.4.2",
 | 
					    "version": "2024.6.0",
 | 
				
			||||||
  "private": true
 | 
					    "private": true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										555
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										555
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@ -1,4 +1,4 @@
 | 
				
			|||||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
 | 
					# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "aiohttp"
 | 
					name = "aiohttp"
 | 
				
			||||||
@ -639,13 +639,13 @@ zstd = ["zstandard (==0.22.0)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "certifi"
 | 
					name = "certifi"
 | 
				
			||||||
version = "2024.2.2"
 | 
					version = "2024.7.4"
 | 
				
			||||||
description = "Python package for providing Mozilla's CA Bundle."
 | 
					description = "Python package for providing Mozilla's CA Bundle."
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.6"
 | 
					python-versions = ">=3.6"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
 | 
					    {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
 | 
				
			||||||
    {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
 | 
					    {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -956,63 +956,63 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "coverage"
 | 
					name = "coverage"
 | 
				
			||||||
version = "7.5.3"
 | 
					version = "7.5.4"
 | 
				
			||||||
description = "Code coverage measurement for Python"
 | 
					description = "Code coverage measurement for Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-win32.whl", hash = "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"},
 | 
					    {file = "coverage-7.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-win32.whl", hash = "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"},
 | 
					    {file = "coverage-7.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-win32.whl", hash = "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"},
 | 
					    {file = "coverage-7.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-win32.whl", hash = "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"},
 | 
					    {file = "coverage-7.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-win32.whl", hash = "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace"},
 | 
				
			||||||
    {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"},
 | 
					    {file = "coverage-7.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d"},
 | 
				
			||||||
    {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"},
 | 
					    {file = "coverage-7.5.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5"},
 | 
				
			||||||
    {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"},
 | 
					    {file = "coverage-7.5.4.tar.gz", hash = "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
@ -1106,33 +1106,33 @@ tests = ["django", "hypothesis", "pytest", "pytest-asyncio"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "debugpy"
 | 
					name = "debugpy"
 | 
				
			||||||
version = "1.8.1"
 | 
					version = "1.8.2"
 | 
				
			||||||
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.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"},
 | 
					    {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"},
 | 
					    {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"},
 | 
					    {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"},
 | 
					    {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"},
 | 
					    {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"},
 | 
					    {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"},
 | 
					    {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"},
 | 
					    {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"},
 | 
					    {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"},
 | 
					    {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"},
 | 
					    {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"},
 | 
					    {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"},
 | 
					    {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"},
 | 
					    {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"},
 | 
					    {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"},
 | 
					    {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"},
 | 
					    {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"},
 | 
					    {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"},
 | 
					    {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"},
 | 
					    {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"},
 | 
				
			||||||
    {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"},
 | 
					    {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"},
 | 
				
			||||||
    {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"},
 | 
					    {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -1425,17 +1425,17 @@ websockets = ["websocket-client (>=1.3.0)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "drf-jsonschema-serializer"
 | 
					name = "drf-jsonschema-serializer"
 | 
				
			||||||
version = "2.0.0"
 | 
					version = "3.0.0"
 | 
				
			||||||
description = "JSON Schema support for Django REST Framework"
 | 
					description = "JSON Schema support for Django REST Framework"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "*"
 | 
					python-versions = ">=3.10"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "drf-jsonschema-serializer-2.0.0.tar.gz", hash = "sha256:bfeea9f2e49f6f14d12ae5a55680657abb70066615739682351589cda67c3f4f"},
 | 
					    {file = "drf_jsonschema_serializer-3.0.0-py3-none-any.whl", hash = "sha256:d0e5cce095a5638b0bb7867aa060ed59ab9eed2f54ba5058dd9b483c9c887ed5"},
 | 
				
			||||||
    {file = "drf_jsonschema_serializer-2.0.0-py3-none-any.whl", hash = "sha256:02b4e4f19d680b0565a4231f1cba29371c01af0e1682944f1e2d1093bbd07d86"},
 | 
					    {file = "drf_jsonschema_serializer-3.0.0.tar.gz", hash = "sha256:8a42c6079225f789cd55321897073b576d15db3406d008e92f44febb017a232a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
django = ">=3.2"
 | 
					django = ">=4.2"
 | 
				
			||||||
djangorestframework = ">=3.13"
 | 
					djangorestframework = ">=3.13"
 | 
				
			||||||
jsonschema = ">=4.0.0"
 | 
					jsonschema = ">=4.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1443,9 +1443,25 @@ jsonschema = ">=4.0.0"
 | 
				
			|||||||
all-format-validators = ["fqdn", "idna", "isoduration", "jsonpointer", "rfc3339-validator", "rfc3987", "uri-template", "webcolors"]
 | 
					all-format-validators = ["fqdn", "idna", "isoduration", "jsonpointer", "rfc3339-validator", "rfc3987", "uri-template", "webcolors"]
 | 
				
			||||||
coverage = ["pytest-cov"]
 | 
					coverage = ["pytest-cov"]
 | 
				
			||||||
docs = ["sphinx", "sphinx-rtd-theme"]
 | 
					docs = ["sphinx", "sphinx-rtd-theme"]
 | 
				
			||||||
release = ["bump2version", "twine"]
 | 
					release = ["bump-my-version", "twine"]
 | 
				
			||||||
tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[compatible-mypy]", "flake8", "fqdn", "idna", "isoduration", "isort", "jsonpointer", "mypy", "pytest", "pytest-django", "rfc3339-validator", "rfc3987", "tox", "types-jsonschema", "uri-template", "webcolors"]
 | 
					tests = ["black", "django-stubs[compatible-mypy]", "djangorestframework-stubs[compatible-mypy]", "flake8", "fqdn", "idna", "isoduration", "isort", "jsonpointer", "mypy", "pytest", "pytest-django", "rfc3339-validator", "rfc3987", "tox", "types-jsonschema", "uri-template", "webcolors"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "drf-orjson-renderer"
 | 
				
			||||||
 | 
					version = "1.7.2"
 | 
				
			||||||
 | 
					description = "Django RestFramework JSON Renderer Backed by orjson"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.6.0"
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "drf_orjson_renderer-1.7.2-py3-none-any.whl", hash = "sha256:4fbf6f91d7032fbf23e31837e4c5cf6950af4e588bf34e9feadc809c67977657"},
 | 
				
			||||||
 | 
					    {file = "drf_orjson_renderer-1.7.2.tar.gz", hash = "sha256:b8a47c38a6eeaf5ffc7a5d53d028b95fa8b340999507681e403a599f0a3be456"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					django = ">=3.2"
 | 
				
			||||||
 | 
					djangorestframework = "*"
 | 
				
			||||||
 | 
					orjson = ">=3.3.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "drf-spectacular"
 | 
					name = "drf-spectacular"
 | 
				
			||||||
version = "0.27.2"
 | 
					version = "0.27.2"
 | 
				
			||||||
@ -1707,13 +1723,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.133.0"
 | 
					version = "2.136.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.133.0.tar.gz", hash = "sha256:293092905b66a046d3187a99ac454e12b00cc2c70444f26eb2f1f9c1a82720b4"},
 | 
					    {file = "google-api-python-client-2.136.0.tar.gz", hash = "sha256:161c722c8864e7ed39393e2b7eea76ef4e1c933a6a59f9d7c70409b6635f225d"},
 | 
				
			||||||
    {file = "google_api_python_client-2.133.0-py2.py3-none-any.whl", hash = "sha256:396fe676ea0dfed066654dcf9f8dea77a1342f9d9bb23bb88e45b7b81e773926"},
 | 
					    {file = "google_api_python_client-2.136.0-py2.py3-none-any.whl", hash = "sha256:5a554c8b5edf0a609b905d89d7ced82e8f6ac31da1e4d8d5684ef63dbc0e49f5"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -2013,6 +2029,21 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link
 | 
				
			|||||||
perf = ["ipython"]
 | 
					perf = ["ipython"]
 | 
				
			||||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
 | 
					testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "importlib-resources"
 | 
				
			||||||
 | 
					version = "6.4.0"
 | 
				
			||||||
 | 
					description = "Read resources from Python packages"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"},
 | 
				
			||||||
 | 
					    {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[package.extras]
 | 
				
			||||||
 | 
					docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
 | 
				
			||||||
 | 
					testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "incremental"
 | 
					name = "incremental"
 | 
				
			||||||
version = "22.10.0"
 | 
					version = "22.10.0"
 | 
				
			||||||
@ -3003,6 +3034,61 @@ files = [
 | 
				
			|||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
opentelemetry-api = "1.25.0"
 | 
					opentelemetry-api = "1.25.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "orjson"
 | 
				
			||||||
 | 
					version = "3.10.3"
 | 
				
			||||||
 | 
					description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
 | 
				
			||||||
 | 
					optional = false
 | 
				
			||||||
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
 | 
					files = [
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"},
 | 
				
			||||||
 | 
					    {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"},
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "outcome"
 | 
					name = "outcome"
 | 
				
			||||||
version = "1.3.0.post0"
 | 
					version = "1.3.0.post0"
 | 
				
			||||||
@ -3073,13 +3159,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pdoc"
 | 
					name = "pdoc"
 | 
				
			||||||
version = "14.5.0"
 | 
					version = "14.5.1"
 | 
				
			||||||
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.5.0-py3-none-any.whl", hash = "sha256:9a8a84e19662610c0620fbe9f2e4174e3b090f8b601ed46348786ebb7517c508"},
 | 
					    {file = "pdoc-14.5.1-py3-none-any.whl", hash = "sha256:fda6365a06e438b43ca72235b58a2e2ecd66445fcc444313f6ebbde4b0abd94b"},
 | 
				
			||||||
    {file = "pdoc-14.5.0.tar.gz", hash = "sha256:79f534dc8a6494638dd6056b78e17a654df7ed34cc92646553ce3a7ba5a4fa4a"},
 | 
					    {file = "pdoc-14.5.1.tar.gz", hash = "sha256:4ddd9c5123a79f511cedffd7231bf91a6e0bd0968610f768342ec5d00b5eefee"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -3306,36 +3392,36 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "psycopg"
 | 
					name = "psycopg"
 | 
				
			||||||
version = "3.1.19"
 | 
					version = "3.2.1"
 | 
				
			||||||
description = "PostgreSQL database adapter for Python"
 | 
					description = "PostgreSQL database adapter for Python"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "psycopg-3.1.19-py3-none-any.whl", hash = "sha256:dca5e5521c859f6606686432ae1c94e8766d29cc91f2ee595378c510cc5b0731"},
 | 
					    {file = "psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175"},
 | 
				
			||||||
    {file = "psycopg-3.1.19.tar.gz", hash = "sha256:92d7b78ad82426cdcf1a0440678209faa890c6e1721361c2f8901f0dccd62961"},
 | 
					    {file = "psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
psycopg-c = {version = "3.1.19", 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 = ">=4.1"
 | 
					typing-extensions = ">=4.4"
 | 
				
			||||||
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 | 
					tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.extras]
 | 
					[package.extras]
 | 
				
			||||||
binary = ["psycopg-binary (==3.1.19)"]
 | 
					binary = ["psycopg-binary (==3.2.1)"]
 | 
				
			||||||
c = ["psycopg-c (==3.1.19)"]
 | 
					c = ["psycopg-c (==3.2.1)"]
 | 
				
			||||||
dev = ["black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=1.4.1)", "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 (>=3.6.2,<4.0)", "mypy (>=1.4.1)", "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.1.19"
 | 
					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.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "psycopg_c-3.1.19.tar.gz", hash = "sha256:8e90f53c430e7d661cb3a9298e2761847212ead1b24c5fb058fc9d0fd9616017"},
 | 
					    {file = "psycopg_c-3.2.1.tar.gz", hash = "sha256:2d09943cc8a855c42c1e23b4298957b7ce8f27bf3683258c52fd139f601f7cda"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -3376,110 +3462,120 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pydantic"
 | 
					name = "pydantic"
 | 
				
			||||||
version = "2.7.4"
 | 
					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.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"},
 | 
					    {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"},
 | 
				
			||||||
    {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"},
 | 
					    {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
annotated-types = ">=0.4.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.18.4"
 | 
					pydantic-core = "2.20.1"
 | 
				
			||||||
typing-extensions = ">=4.6.1"
 | 
					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)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "pydantic-core"
 | 
					name = "pydantic-core"
 | 
				
			||||||
version = "2.18.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.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"},
 | 
					    {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"},
 | 
					    {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"},
 | 
					    {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"},
 | 
					    {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"},
 | 
					    {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"},
 | 
					    {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"},
 | 
					    {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.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"},
 | 
				
			||||||
    {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"},
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"},
 | 
				
			||||||
 | 
					    {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.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"},
 | 
				
			||||||
 | 
					    {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4052,28 +4148,29 @@ pyasn1 = ">=0.1.3"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "ruff"
 | 
					name = "ruff"
 | 
				
			||||||
version = "0.4.9"
 | 
					version = "0.5.1"
 | 
				
			||||||
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.4.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b262ed08d036ebe162123170b35703aaf9daffecb698cd367a8d585157732991"},
 | 
					    {file = "ruff-0.5.1-py3-none-linux_armv6l.whl", hash = "sha256:6ecf968fcf94d942d42b700af18ede94b07521bd188aaf2cd7bc898dd8cb63b6"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:98ec2775fd2d856dc405635e5ee4ff177920f2141b8e2d9eb5bd6efd50e80317"},
 | 
					    {file = "ruff-0.5.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:204fb0a472f00f2e6280a7c8c7c066e11e20e23a37557d63045bf27a616ba61c"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4555056049d46d8a381f746680db1c46e67ac3b00d714606304077682832998e"},
 | 
					    {file = "ruff-0.5.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d235968460e8758d1e1297e1de59a38d94102f60cafb4d5382033c324404ee9d"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38beace10b8d5f9b6bdc91619310af6d63dd2019f3fb2d17a2da26360d7962fa"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e478d2f09cf06add143cf8c4540ef77b6599191e0c50ed976582f06e588c994"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2d45ddc6d82e1190ea737341326ecbc9a61447ba331b0a8962869fcada758505"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0368d765eec8247b8550251c49ebb20554cc4e812f383ff9f5bf0d5d94190b0"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78de3fdb95c4af084087628132336772b1c5044f6e710739d440fc0bccf4d321"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3a9a9a1b582e37669b0138b7c1d9d60b9edac880b80eb2baba6d0e566bdeca4d"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdd9f723e16003623423affabcc0a807a66552ee6a29f90eddad87a40c750b78"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88bffe9c6a454bf8529f9ab9091c99490578a593cc9f9822b7fc065ee0712a06"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be9fd62c1e99539da05fcdc1e90d20f74aec1b7a1613463ed77870057cd6bd96"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:673bddb893f21ab47a8334c8e0ea7fd6598ecc8e698da75bcd12a7b9d0a3206e"},
 | 
					    {file = "ruff-0.5.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e216fc75a80ea1fbd96af94a6233d90190d5b65cc3d5dfacf2bd48c3e067d3e1"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c1aff58c31948cc66d0b22951aa19edb5af0a3af40c936340cd32a8b1ab7438"},
 | 
					    {file = "ruff-0.5.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c4c2112e9883a40967827d5c24803525145e7dab315497fae149764979ac7929"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:784d3ec9bd6493c3b720a0b76f741e6c2d7d44f6b2be87f5eef1ae8cc1d54c84"},
 | 
					    {file = "ruff-0.5.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dfaf11c8a116394da3b65cd4b36de30d8552fa45b8119b9ef5ca6638ab964fa3"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:732dd550bfa5d85af8c3c6cbc47ba5b67c6aed8a89e2f011b908fc88f87649db"},
 | 
					    {file = "ruff-0.5.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d7ceb9b2fe700ee09a0c6b192c5ef03c56eb82a0514218d8ff700f6ade004108"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-win32.whl", hash = "sha256:8064590fd1a50dcf4909c268b0e7c2498253273309ad3d97e4a752bb9df4f521"},
 | 
					    {file = "ruff-0.5.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bac6288e82f6296f82ed5285f597713acb2a6ae26618ffc6b429c597b392535c"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-win_amd64.whl", hash = "sha256:e0a22c4157e53d006530c902107c7f550b9233e9706313ab57b892d7197d8e52"},
 | 
					    {file = "ruff-0.5.1-py3-none-win32.whl", hash = "sha256:5c441d9c24ec09e1cb190a04535c5379b36b73c4bc20aa180c54812c27d1cca4"},
 | 
				
			||||||
    {file = "ruff-0.4.9-py3-none-win_arm64.whl", hash = "sha256:5d5460f789ccf4efd43f265a58538a2c24dbce15dbf560676e430375f20a8198"},
 | 
					    {file = "ruff-0.5.1-py3-none-win_amd64.whl", hash = "sha256:b1789bf2cd3d1b5a7d38397cac1398ddf3ad7f73f4de01b1e913e2abc7dfc51d"},
 | 
				
			||||||
    {file = "ruff-0.4.9.tar.gz", hash = "sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3"},
 | 
					    {file = "ruff-0.5.1-py3-none-win_arm64.whl", hash = "sha256:2875b7596a740cbbd492f32d24be73e545a4ce0a3daf51e4f4e609962bfd3cd2"},
 | 
				
			||||||
 | 
					    {file = "ruff-0.5.1.tar.gz", hash = "sha256:3164488aebd89b1745b47fd00604fb4358d774465f20d1fcd907f9c0fc1b0655"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@ -4112,13 +4209,13 @@ django-query = ["django (>=3.2)"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "selenium"
 | 
					name = "selenium"
 | 
				
			||||||
version = "4.21.0"
 | 
					version = "4.22.0"
 | 
				
			||||||
description = ""
 | 
					description = "Official Python bindings for Selenium WebDriver"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.8"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "selenium-4.21.0-py3-none-any.whl", hash = "sha256:4770ffe5a5264e609de7dc914be6b89987512040d5a8efb2abb181330d097993"},
 | 
					    {file = "selenium-4.22.0-py3-none-any.whl", hash = "sha256:e424991196e9857e19bf04fe5c1c0a4aac076794ff5e74615b1124e729d93104"},
 | 
				
			||||||
    {file = "selenium-4.21.0.tar.gz", hash = "sha256:650dbfa5159895ff00ad16e5ddb6ceecb86b90c7ed2012b3f041f64e6e4904fe"},
 | 
					    {file = "selenium-4.22.0.tar.gz", hash = "sha256:903c8c9d61b3eea6fcc9809dc7d9377e04e2ac87709876542cc8f863e482c4ce"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4127,16 +4224,17 @@ trio = ">=0.17,<1.0"
 | 
				
			|||||||
trio-websocket = ">=0.9,<1.0"
 | 
					trio-websocket = ">=0.9,<1.0"
 | 
				
			||||||
typing_extensions = ">=4.9.0"
 | 
					typing_extensions = ">=4.9.0"
 | 
				
			||||||
urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
 | 
					urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
 | 
				
			||||||
 | 
					websocket-client = ">=1.8.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "sentry-sdk"
 | 
					name = "sentry-sdk"
 | 
				
			||||||
version = "2.5.1"
 | 
					version = "2.7.1"
 | 
				
			||||||
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.5.1-py2.py3-none-any.whl", hash = "sha256:1f87acdce4a43a523ae5aa21a3fc37522d73ebd9ec04b1dbf01aa3d173852def"},
 | 
					    {file = "sentry_sdk-2.7.1-py2.py3-none-any.whl", hash = "sha256:ef1b3d54eb715825657cd4bb3cb42bb4dc85087bac14c56b0fd8c21abd968c9a"},
 | 
				
			||||||
    {file = "sentry_sdk-2.5.1.tar.gz", hash = "sha256:fbc40a78a8a9c6675133031116144f0d0940376fa6e4e1acd5624c90b0aaf58b"},
 | 
					    {file = "sentry_sdk-2.7.1.tar.gz", hash = "sha256:25006c7e68b75aaa5e6b9c6a420ece22e8d7daec4b7a906ffd3a8607b67c037b"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4166,7 +4264,7 @@ langchain = ["langchain (>=0.0.210)"]
 | 
				
			|||||||
loguru = ["loguru (>=0.5)"]
 | 
					loguru = ["loguru (>=0.5)"]
 | 
				
			||||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
 | 
					openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
 | 
				
			||||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
 | 
					opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
 | 
				
			||||||
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
 | 
					opentelemetry-experimental = ["opentelemetry-instrumentation-aio-pika (==0.46b0)", "opentelemetry-instrumentation-aiohttp-client (==0.46b0)", "opentelemetry-instrumentation-aiopg (==0.46b0)", "opentelemetry-instrumentation-asgi (==0.46b0)", "opentelemetry-instrumentation-asyncio (==0.46b0)", "opentelemetry-instrumentation-asyncpg (==0.46b0)", "opentelemetry-instrumentation-aws-lambda (==0.46b0)", "opentelemetry-instrumentation-boto (==0.46b0)", "opentelemetry-instrumentation-boto3sqs (==0.46b0)", "opentelemetry-instrumentation-botocore (==0.46b0)", "opentelemetry-instrumentation-cassandra (==0.46b0)", "opentelemetry-instrumentation-celery (==0.46b0)", "opentelemetry-instrumentation-confluent-kafka (==0.46b0)", "opentelemetry-instrumentation-dbapi (==0.46b0)", "opentelemetry-instrumentation-django (==0.46b0)", "opentelemetry-instrumentation-elasticsearch (==0.46b0)", "opentelemetry-instrumentation-falcon (==0.46b0)", "opentelemetry-instrumentation-fastapi (==0.46b0)", "opentelemetry-instrumentation-flask (==0.46b0)", "opentelemetry-instrumentation-grpc (==0.46b0)", "opentelemetry-instrumentation-httpx (==0.46b0)", "opentelemetry-instrumentation-jinja2 (==0.46b0)", "opentelemetry-instrumentation-kafka-python (==0.46b0)", "opentelemetry-instrumentation-logging (==0.46b0)", "opentelemetry-instrumentation-mysql (==0.46b0)", "opentelemetry-instrumentation-mysqlclient (==0.46b0)", "opentelemetry-instrumentation-pika (==0.46b0)", "opentelemetry-instrumentation-psycopg (==0.46b0)", "opentelemetry-instrumentation-psycopg2 (==0.46b0)", "opentelemetry-instrumentation-pymemcache (==0.46b0)", "opentelemetry-instrumentation-pymongo (==0.46b0)", "opentelemetry-instrumentation-pymysql (==0.46b0)", "opentelemetry-instrumentation-pyramid (==0.46b0)", "opentelemetry-instrumentation-redis (==0.46b0)", "opentelemetry-instrumentation-remoulade (==0.46b0)", "opentelemetry-instrumentation-requests (==0.46b0)", "opentelemetry-instrumentation-sklearn (==0.46b0)", "opentelemetry-instrumentation-sqlalchemy (==0.46b0)", "opentelemetry-instrumentation-sqlite3 (==0.46b0)", "opentelemetry-instrumentation-starlette (==0.46b0)", "opentelemetry-instrumentation-system-metrics (==0.46b0)", "opentelemetry-instrumentation-threading (==0.46b0)", "opentelemetry-instrumentation-tornado (==0.46b0)", "opentelemetry-instrumentation-tortoiseorm (==0.46b0)", "opentelemetry-instrumentation-urllib (==0.46b0)", "opentelemetry-instrumentation-urllib3 (==0.46b0)", "opentelemetry-instrumentation-wsgi (==0.46b0)"]
 | 
				
			||||||
pure-eval = ["asttokens", "executing", "pure-eval"]
 | 
					pure-eval = ["asttokens", "executing", "pure-eval"]
 | 
				
			||||||
pymongo = ["pymongo (>=3.1)"]
 | 
					pymongo = ["pymongo (>=3.1)"]
 | 
				
			||||||
pyspark = ["pyspark (>=2.4.4)"]
 | 
					pyspark = ["pyspark (>=2.4.4)"]
 | 
				
			||||||
@ -4176,7 +4274,7 @@ sanic = ["sanic (>=0.8)"]
 | 
				
			|||||||
sqlalchemy = ["sqlalchemy (>=1.2)"]
 | 
					sqlalchemy = ["sqlalchemy (>=1.2)"]
 | 
				
			||||||
starlette = ["starlette (>=0.19.1)"]
 | 
					starlette = ["starlette (>=0.19.1)"]
 | 
				
			||||||
starlite = ["starlite (>=1.48)"]
 | 
					starlite = ["starlite (>=1.48)"]
 | 
				
			||||||
tornado = ["tornado (>=5)"]
 | 
					tornado = ["tornado (>=6)"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "service-identity"
 | 
					name = "service-identity"
 | 
				
			||||||
@ -4421,16 +4519,17 @@ typing = ["mypy (>=1.4)", "rich", "twisted"]
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "swagger-spec-validator"
 | 
					name = "swagger-spec-validator"
 | 
				
			||||||
version = "3.0.3"
 | 
					version = "3.0.4"
 | 
				
			||||||
description = "Validation of Swagger specifications"
 | 
					description = "Validation of Swagger specifications"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = ">=3.7"
 | 
					python-versions = ">=3.8"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "swagger-spec-validator-3.0.3.tar.gz", hash = "sha256:16a5ce08c772824a77b1a4a05efc047d72eef1ed53fb969dfe0a18f437ac30a8"},
 | 
					    {file = "swagger_spec_validator-3.0.4-py2.py3-none-any.whl", hash = "sha256:1a2a4f4f7076479ae7835d892dd53952ccca9414efa172c440c775cf0ac01f48"},
 | 
				
			||||||
    {file = "swagger_spec_validator-3.0.3-py2.py3-none-any.whl", hash = "sha256:174b5de4ab0899df9a57d35c880aaa515511c4b8b578d9d519b09a9596537055"},
 | 
					    {file = "swagger_spec_validator-3.0.4.tar.gz", hash = "sha256:637ac6d865270bfcd07df24605548e6e1f1d9c39adcfd855da37fa3fdebfed4b"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
 | 
					importlib-resources = ">=1.3"
 | 
				
			||||||
jsonschema = "*"
 | 
					jsonschema = "*"
 | 
				
			||||||
pyyaml = "*"
 | 
					pyyaml = "*"
 | 
				
			||||||
typing-extensions = "*"
 | 
					typing-extensions = "*"
 | 
				
			||||||
@ -4505,13 +4604,13 @@ wsproto = ">=0.14"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "twilio"
 | 
					name = "twilio"
 | 
				
			||||||
version = "9.1.1"
 | 
					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.1.1-py2.py3-none-any.whl", hash = "sha256:cc3e090c3884db7d70e7c647358b9cf1f4d30fd3fbe0412adcae0df8459d29b0"},
 | 
					    {file = "twilio-9.2.3-py2.py3-none-any.whl", hash = "sha256:76bfc39aa8d854510907cb7f9465814dfdea9e91ec199bb44f0785f05746f4cc"},
 | 
				
			||||||
    {file = "twilio-9.1.1.tar.gz", hash = "sha256:cfe72b12cabac2f0997f1060d53cea14bd1196e2cbda14789e53c7dd762c4349"},
 | 
					    {file = "twilio-9.2.3.tar.gz", hash = "sha256:da2255b5f3753cb3bf647fc6c50edbdb367ebc3cde6802806f6f863058a65f75"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -4891,13 +4990,13 @@ files = [
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "webauthn"
 | 
					name = "webauthn"
 | 
				
			||||||
version = "2.1.0"
 | 
					version = "2.2.0"
 | 
				
			||||||
description = "Pythonic WebAuthn"
 | 
					description = "Pythonic WebAuthn"
 | 
				
			||||||
optional = false
 | 
					optional = false
 | 
				
			||||||
python-versions = "*"
 | 
					python-versions = "*"
 | 
				
			||||||
files = [
 | 
					files = [
 | 
				
			||||||
    {file = "webauthn-2.1.0-py3-none-any.whl", hash = "sha256:9e1cf916e5ed7c01d54a6dfcc19dacbd2b87b81d2648f001b1fcbcb7aa2ff130"},
 | 
					    {file = "webauthn-2.2.0-py3-none-any.whl", hash = "sha256:e8e2daace85dde8f6fb436c1bca9aa72d5931dac8829ecc1562cc4e7cc169f6c"},
 | 
				
			||||||
    {file = "webauthn-2.1.0.tar.gz", hash = "sha256:b196a4246c2818820857ba195c6e6e5398c761117f2269e3d2deab11c7995fc4"},
 | 
					    {file = "webauthn-2.2.0.tar.gz", hash = "sha256:70e4f318d293125e3a8609838be0561119f4f8846bc430d524f8da4052ee18cc"},
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[package.dependencies]
 | 
					[package.dependencies]
 | 
				
			||||||
@ -5350,4 +5449,4 @@ files = [
 | 
				
			|||||||
[metadata]
 | 
					[metadata]
 | 
				
			||||||
lock-version = "2.0"
 | 
					lock-version = "2.0"
 | 
				
			||||||
python-versions = "~3.12"
 | 
					python-versions = "~3.12"
 | 
				
			||||||
content-hash = "f960013b56683ab42d82f8b49b2822dffc76046e3d22695ebb737b405a98dbaf"
 | 
					content-hash = "484d2f95c5c725b76ade4455d4416be3ad6d29b1721a9367a775ecc442c7cc4b"
 | 
				
			||||||
 | 
				
			|||||||
@ -7,10 +7,9 @@ ENV NODE_ENV=production
 | 
				
			|||||||
WORKDIR /static
 | 
					WORKDIR /static
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COPY package.json /
 | 
					COPY package.json /
 | 
				
			||||||
COPY web/package.json .
 | 
					 | 
				
			||||||
COPY web/package-lock.json .
 | 
					 | 
				
			||||||
RUN --mount=type=bind,target=/static/package.json,src=./web/package.json \
 | 
					RUN --mount=type=bind,target=/static/package.json,src=./web/package.json \
 | 
				
			||||||
    --mount=type=bind,target=/static/package-lock.json,src=./web/package-lock.json \
 | 
					    --mount=type=bind,target=/static/package-lock.json,src=./web/package-lock.json \
 | 
				
			||||||
 | 
					    --mount=type=bind,target=/static/scripts,src=./web/scripts \
 | 
				
			||||||
    --mount=type=cache,target=/root/.npm \
 | 
					    --mount=type=cache,target=/root/.npm \
 | 
				
			||||||
    npm ci --include=dev
 | 
					    npm ci --include=dev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
[tool.poetry]
 | 
					[tool.poetry]
 | 
				
			||||||
name = "authentik"
 | 
					name = "authentik"
 | 
				
			||||||
version = "2024.4.2"
 | 
					version = "2024.6.0"
 | 
				
			||||||
description = ""
 | 
					description = ""
 | 
				
			||||||
authors = ["authentik Team <hello@goauthentik.io>"]
 | 
					authors = ["authentik Team <hello@goauthentik.io>"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,7 +32,7 @@ select = [
 | 
				
			|||||||
    "PL",
 | 
					    "PL",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
ignore = [
 | 
					ignore = [
 | 
				
			||||||
    "DJ001" # Avoid using `null=True` on string-based fields,
 | 
					    "DJ001", # Avoid using `null=True` on string-based fields,
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
[tool.ruff.lint.pylint]
 | 
					[tool.ruff.lint.pylint]
 | 
				
			||||||
max-args = 7
 | 
					max-args = 7
 | 
				
			||||||
@ -103,10 +103,11 @@ django-prometheus = "*"
 | 
				
			|||||||
django-redis = "*"
 | 
					django-redis = "*"
 | 
				
			||||||
django-storages = { extras = ["s3"], version = "*" }
 | 
					django-storages = { extras = ["s3"], version = "*" }
 | 
				
			||||||
# See https://github.com/django-tenants/django-tenants/pull/997
 | 
					# See https://github.com/django-tenants/django-tenants/pull/997
 | 
				
			||||||
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch="authentik-fixes" }
 | 
					django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
 | 
				
			||||||
djangorestframework = "3.14.0"
 | 
					djangorestframework = "3.14.0"
 | 
				
			||||||
djangorestframework-guardian = "*"
 | 
					djangorestframework-guardian = "*"
 | 
				
			||||||
docker = "*"
 | 
					docker = "*"
 | 
				
			||||||
 | 
					drf-orjson-renderer = "*"
 | 
				
			||||||
drf-spectacular = "*"
 | 
					drf-spectacular = "*"
 | 
				
			||||||
dumb-init = "*"
 | 
					dumb-init = "*"
 | 
				
			||||||
duo-client = "*"
 | 
					duo-client = "*"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							@ -1,7 +1,7 @@
 | 
				
			|||||||
openapi: 3.0.3
 | 
					openapi: 3.0.3
 | 
				
			||||||
info:
 | 
					info:
 | 
				
			||||||
  title: authentik
 | 
					  title: authentik
 | 
				
			||||||
  version: 2024.4.2
 | 
					  version: 2024.6.0
 | 
				
			||||||
  description: Making authentication simple.
 | 
					  description: Making authentication simple.
 | 
				
			||||||
  contact:
 | 
					  contact:
 | 
				
			||||||
    email: hello@goauthentik.io
 | 
					    email: hello@goauthentik.io
 | 
				
			||||||
@ -13080,6 +13080,15 @@ paths:
 | 
				
			|||||||
        name: identifier
 | 
					        name: identifier
 | 
				
			||||||
        schema:
 | 
					        schema:
 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
 | 
					      - in: query
 | 
				
			||||||
 | 
					        name: identifier_in
 | 
				
			||||||
 | 
					        schema:
 | 
				
			||||||
 | 
					          type: array
 | 
				
			||||||
 | 
					          items:
 | 
				
			||||||
 | 
					            type: string
 | 
				
			||||||
 | 
					        description: Multiple values may be separated by commas.
 | 
				
			||||||
 | 
					        explode: false
 | 
				
			||||||
 | 
					        style: form
 | 
				
			||||||
      - in: query
 | 
					      - in: query
 | 
				
			||||||
        name: ip
 | 
					        name: ip
 | 
				
			||||||
        schema:
 | 
					        schema:
 | 
				
			||||||
@ -36625,6 +36634,7 @@ components:
 | 
				
			|||||||
        href:
 | 
					        href:
 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
          readOnly: true
 | 
					          readOnly: true
 | 
				
			||||||
 | 
					          nullable: true
 | 
				
			||||||
        name:
 | 
					        name:
 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
          readOnly: true
 | 
					          readOnly: true
 | 
				
			||||||
@ -39488,6 +39498,9 @@ components:
 | 
				
			|||||||
          allOf:
 | 
					          allOf:
 | 
				
			||||||
          - $ref: '#/components/schemas/ServiceConnection'
 | 
					          - $ref: '#/components/schemas/ServiceConnection'
 | 
				
			||||||
          readOnly: true
 | 
					          readOnly: true
 | 
				
			||||||
 | 
					        refresh_interval_s:
 | 
				
			||||||
 | 
					          type: integer
 | 
				
			||||||
 | 
					          readOnly: true
 | 
				
			||||||
        token_identifier:
 | 
					        token_identifier:
 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
          description: Get Token identifier
 | 
					          description: Get Token identifier
 | 
				
			||||||
@ -39509,6 +39522,7 @@ components:
 | 
				
			|||||||
      - pk
 | 
					      - pk
 | 
				
			||||||
      - providers
 | 
					      - providers
 | 
				
			||||||
      - providers_obj
 | 
					      - providers_obj
 | 
				
			||||||
 | 
					      - refresh_interval_s
 | 
				
			||||||
      - service_connection_obj
 | 
					      - service_connection_obj
 | 
				
			||||||
      - token_identifier
 | 
					      - token_identifier
 | 
				
			||||||
      - type
 | 
					      - type
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										23
									
								
								tests/e2e/test-saml-idp/saml20-sp-remote.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/e2e/test-saml-idp/saml20-sp-remote.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * SAML 2.0 remote SP metadata for SimpleSAMLphp.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See: https://simplesamlphp.org/docs/stable/simplesamlphp-reference-sp-remote
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')] = array(
 | 
				
			||||||
 | 
					    'AssertionConsumerService' => getenv('SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE'),
 | 
				
			||||||
 | 
					    'SingleLogoutService' => getenv('SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE'),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (null != getenv('SIMPLESAMLPHP_SP_NAME_ID_FORMAT')) {
 | 
				
			||||||
 | 
					    $metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')] = array_merge($metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')], array('NameIDFormat' => getenv('SIMPLESAMLPHP_SP_NAME_ID_FORMAT')));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (null != getenv('SIMPLESAMLPHP_SP_NAME_ID_ATTRIBUTE')) {
 | 
				
			||||||
 | 
					    $metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')] = array_merge($metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')], array('simplesaml.nameidattribute' => getenv('SIMPLESAMLPHP_SP_NAME_ID_ATTRIBUTE')));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (null != getenv('SIMPLESAMLPHP_SP_SIGN_ASSERTION')) {
 | 
				
			||||||
 | 
					    $metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')] = array_merge($metadata[getenv('SIMPLESAMLPHP_SP_ENTITY_ID')], array('saml20.sign.assertion' => ('true' == getenv('SIMPLESAMLPHP_SP_SIGN_ASSERTION'))));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -5,7 +5,6 @@ from time import sleep
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from docker.client import DockerClient, from_env
 | 
					from docker.client import DockerClient, from_env
 | 
				
			||||||
from docker.models.containers import Container
 | 
					from docker.models.containers import Container
 | 
				
			||||||
from guardian.shortcuts import get_anonymous_user
 | 
					 | 
				
			||||||
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
 | 
					from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
 | 
				
			||||||
from ldap3.core.exceptions import LDAPInvalidCredentialsResult
 | 
					from ldap3.core.exceptions import LDAPInvalidCredentialsResult
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -180,15 +179,13 @@ class TestProviderLDAP(SeleniumTestCase):
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        with self.assertRaises(LDAPInvalidCredentialsResult):
 | 
					        with self.assertRaises(LDAPInvalidCredentialsResult):
 | 
				
			||||||
            _connection.bind()
 | 
					            _connection.bind()
 | 
				
			||||||
        anon = get_anonymous_user()
 | 
					 | 
				
			||||||
        self.assertTrue(
 | 
					        self.assertTrue(
 | 
				
			||||||
            Event.objects.filter(
 | 
					            Event.objects.filter(
 | 
				
			||||||
                action=EventAction.LOGIN_FAILED,
 | 
					                action=EventAction.LOGIN_FAILED,
 | 
				
			||||||
                user={
 | 
					                user={
 | 
				
			||||||
                    "pk": anon.pk,
 | 
					                    "pk": self.user.pk,
 | 
				
			||||||
                    "email": anon.email,
 | 
					                    "email": self.user.email,
 | 
				
			||||||
                    "username": anon.username,
 | 
					                    "username": self.user.username,
 | 
				
			||||||
                    "is_anonymous": True,
 | 
					 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            ).exists(),
 | 
					            ).exists(),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
"""test OAuth Source"""
 | 
					"""test OAuth Source"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from json import loads
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
@ -194,3 +195,41 @@ class TestSourceOAuth2(SeleniumTestCase):
 | 
				
			|||||||
        self.driver.get(self.if_user_url("/settings"))
 | 
					        self.driver.get(self.if_user_url("/settings"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assert_user(User(username="foo", name="admin", email="admin@example.com"))
 | 
					        self.assert_user(User(username="foo", name="admin", email="admin@example.com"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @retry()
 | 
				
			||||||
 | 
					    @apply_blueprint(
 | 
				
			||||||
 | 
					        "default/flow-default-authentication-flow.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-invalidation-flow.yaml",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    @apply_blueprint(
 | 
				
			||||||
 | 
					        "default/flow-default-source-authentication.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-source-enrollment.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-source-pre-authentication.yaml",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    def test_oauth_link(self):
 | 
				
			||||||
 | 
					        """test OAuth Source link OIDC"""
 | 
				
			||||||
 | 
					        self.create_objects()
 | 
				
			||||||
 | 
					        self.driver.get(self.live_server_url)
 | 
				
			||||||
 | 
					        self.login()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.driver.get(
 | 
				
			||||||
 | 
					            self.url("authentik_sources_oauth:oauth-client-login", source_slug=self.slug)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Now we should be at the IDP, wait for the login field
 | 
				
			||||||
 | 
					        self.wait.until(ec.presence_of_element_located((By.ID, "login")))
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "login").send_keys("admin@example.com")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys("password")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Wait until we're logged in
 | 
				
			||||||
 | 
					        self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "button[type=submit]")))
 | 
				
			||||||
 | 
					        self.driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.driver.get(self.url("authentik_api:usersourceconnection-list") + "?format=json")
 | 
				
			||||||
 | 
					        body_json = loads(self.driver.find_element(By.CSS_SELECTOR, "pre").text)
 | 
				
			||||||
 | 
					        results = body_json["results"]
 | 
				
			||||||
 | 
					        self.assertEqual(len(results), 1)
 | 
				
			||||||
 | 
					        connection = results[0]
 | 
				
			||||||
 | 
					        self.assertEqual(connection["source"]["slug"], self.slug)
 | 
				
			||||||
 | 
					        self.assertEqual(connection["user"], self.user.pk)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
"""test SAML Source"""
 | 
					"""test SAML Source"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
from time import sleep
 | 
					from time import sleep
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -88,8 +89,20 @@ class TestSourceSAML(SeleniumTestCase):
 | 
				
			|||||||
                interval=5 * 1_000 * 1_000_000,
 | 
					                interval=5 * 1_000 * 1_000_000,
 | 
				
			||||||
                start_period=1 * 1_000 * 1_000_000,
 | 
					                start_period=1 * 1_000 * 1_000_000,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            "volumes": {
 | 
				
			||||||
 | 
					                str(
 | 
				
			||||||
 | 
					                    (Path(__file__).parent / Path("test-saml-idp/saml20-sp-remote.php")).absolute()
 | 
				
			||||||
 | 
					                ): {
 | 
				
			||||||
 | 
					                    "bind": "/var/www/simplesamlphp/metadata/saml20-sp-remote.php",
 | 
				
			||||||
 | 
					                    "mode": "ro",
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            "environment": {
 | 
					            "environment": {
 | 
				
			||||||
                "SIMPLESAMLPHP_SP_ENTITY_ID": "entity-id",
 | 
					                "SIMPLESAMLPHP_SP_ENTITY_ID": "entity-id",
 | 
				
			||||||
 | 
					                "SIMPLESAMLPHP_SP_NAME_ID_FORMAT": (
 | 
				
			||||||
 | 
					                    "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                "SIMPLESAMLPHP_SP_NAME_ID_ATTRIBUTE": "email",
 | 
				
			||||||
                "SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE": (
 | 
					                "SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE": (
 | 
				
			||||||
                    self.url("authentik_sources_saml:acs", source_slug=self.slug)
 | 
					                    self.url("authentik_sources_saml:acs", source_slug=self.slug)
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
@ -318,3 +331,109 @@ class TestSourceSAML(SeleniumTestCase):
 | 
				
			|||||||
            .exclude(pk=self.user.pk)
 | 
					            .exclude(pk=self.user.pk)
 | 
				
			||||||
            .first()
 | 
					            .first()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @retry()
 | 
				
			||||||
 | 
					    @apply_blueprint(
 | 
				
			||||||
 | 
					        "default/flow-default-authentication-flow.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-invalidation-flow.yaml",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    @apply_blueprint(
 | 
				
			||||||
 | 
					        "default/flow-default-source-authentication.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-source-enrollment.yaml",
 | 
				
			||||||
 | 
					        "default/flow-default-source-pre-authentication.yaml",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    def test_idp_post_auto_enroll_auth(self):
 | 
				
			||||||
 | 
					        """test SAML Source With post binding (auto redirect)"""
 | 
				
			||||||
 | 
					        # Bootstrap all needed objects
 | 
				
			||||||
 | 
					        authentication_flow = Flow.objects.get(slug="default-source-authentication")
 | 
				
			||||||
 | 
					        enrollment_flow = Flow.objects.get(slug="default-source-enrollment")
 | 
				
			||||||
 | 
					        pre_authentication_flow = Flow.objects.get(slug="default-source-pre-authentication")
 | 
				
			||||||
 | 
					        keypair = CertificateKeyPair.objects.create(
 | 
				
			||||||
 | 
					            name=generate_id(),
 | 
				
			||||||
 | 
					            certificate_data=IDP_CERT,
 | 
				
			||||||
 | 
					            key_data=IDP_KEY,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        source = SAMLSource.objects.create(
 | 
				
			||||||
 | 
					            name=generate_id(),
 | 
				
			||||||
 | 
					            slug=self.slug,
 | 
				
			||||||
 | 
					            authentication_flow=authentication_flow,
 | 
				
			||||||
 | 
					            enrollment_flow=enrollment_flow,
 | 
				
			||||||
 | 
					            pre_authentication_flow=pre_authentication_flow,
 | 
				
			||||||
 | 
					            issuer="entity-id",
 | 
				
			||||||
 | 
					            sso_url=f"http://{self.host}:8080/simplesaml/saml2/idp/SSOService.php",
 | 
				
			||||||
 | 
					            binding_type=SAMLBindingTypes.POST_AUTO,
 | 
				
			||||||
 | 
					            signing_kp=keypair,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        ident_stage = IdentificationStage.objects.first()
 | 
				
			||||||
 | 
					        ident_stage.sources.set([source])
 | 
				
			||||||
 | 
					        ident_stage.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.driver.get(self.live_server_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flow_executor = self.get_shadow_root("ak-flow-executor")
 | 
				
			||||||
 | 
					        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
 | 
				
			||||||
 | 
					        wait = WebDriverWait(identification_stage, self.wait_timeout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait.until(
 | 
				
			||||||
 | 
					            ec.presence_of_element_located(
 | 
				
			||||||
 | 
					                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        identification_stage.find_element(
 | 
				
			||||||
 | 
					            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
 | 
				
			||||||
 | 
					        ).click()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Now we should be at the IDP, wait for the username field
 | 
				
			||||||
 | 
					        self.wait.until(ec.presence_of_element_located((By.ID, "username")))
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "username").send_keys("user1")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys("user1pass")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Wait until we're logged in
 | 
				
			||||||
 | 
					        self.wait_for_url(self.if_user_url("/library"))
 | 
				
			||||||
 | 
					        self.driver.get(self.if_user_url("/settings"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assert_user(
 | 
				
			||||||
 | 
					            User.objects.exclude(username="akadmin")
 | 
				
			||||||
 | 
					            .exclude(username__startswith="ak-outpost")
 | 
				
			||||||
 | 
					            .exclude_anonymous()
 | 
				
			||||||
 | 
					            .exclude(pk=self.user.pk)
 | 
				
			||||||
 | 
					            .first()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Clear all cookies and log in again
 | 
				
			||||||
 | 
					        self.driver.delete_all_cookies()
 | 
				
			||||||
 | 
					        self.driver.get(self.live_server_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flow_executor = self.get_shadow_root("ak-flow-executor")
 | 
				
			||||||
 | 
					        identification_stage = self.get_shadow_root("ak-stage-identification", flow_executor)
 | 
				
			||||||
 | 
					        wait = WebDriverWait(identification_stage, self.wait_timeout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        wait.until(
 | 
				
			||||||
 | 
					            ec.presence_of_element_located(
 | 
				
			||||||
 | 
					                (By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button")
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        identification_stage.find_element(
 | 
				
			||||||
 | 
					            By.CSS_SELECTOR, ".pf-c-login__main-footer-links-item > button"
 | 
				
			||||||
 | 
					        ).click()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Now we should be at the IDP, wait for the username field
 | 
				
			||||||
 | 
					        self.wait.until(ec.presence_of_element_located((By.ID, "username")))
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "username").send_keys("user1")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys("user1pass")
 | 
				
			||||||
 | 
					        self.driver.find_element(By.ID, "password").send_keys(Keys.ENTER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Wait until we're logged in
 | 
				
			||||||
 | 
					        self.wait_for_url(self.if_user_url("/library"))
 | 
				
			||||||
 | 
					        self.driver.get(self.if_user_url("/settings"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # sleep(999999)
 | 
				
			||||||
 | 
					        self.assert_user(
 | 
				
			||||||
 | 
					            User.objects.exclude(username="akadmin")
 | 
				
			||||||
 | 
					            .exclude(username__startswith="ak-outpost")
 | 
				
			||||||
 | 
					            .exclude_anonymous()
 | 
				
			||||||
 | 
					            .exclude(pk=self.user.pk)
 | 
				
			||||||
 | 
					            .first()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										154
									
								
								tests/wdio/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										154
									
								
								tests/wdio/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -6,23 +6,23 @@
 | 
				
			|||||||
        "": {
 | 
					        "": {
 | 
				
			||||||
            "name": "@goauthentik/web-tests",
 | 
					            "name": "@goauthentik/web-tests",
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "chromedriver": "^126.0.1"
 | 
					                "chromedriver": "^126.0.4"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "devDependencies": {
 | 
					            "devDependencies": {
 | 
				
			||||||
                "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
					                "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
				
			||||||
                "@typescript-eslint/eslint-plugin": "^7.5.0",
 | 
					                "@typescript-eslint/eslint-plugin": "^7.5.0",
 | 
				
			||||||
                "@typescript-eslint/parser": "^7.5.0",
 | 
					                "@typescript-eslint/parser": "^7.5.0",
 | 
				
			||||||
                "@wdio/cli": "^8.38.2",
 | 
					                "@wdio/cli": "^8.39.1",
 | 
				
			||||||
                "@wdio/local-runner": "^8.38.2",
 | 
					                "@wdio/local-runner": "^8.39.1",
 | 
				
			||||||
                "@wdio/mocha-framework": "^8.38.2",
 | 
					                "@wdio/mocha-framework": "^8.39.0",
 | 
				
			||||||
                "@wdio/spec-reporter": "^8.38.2",
 | 
					                "@wdio/spec-reporter": "^8.39.0",
 | 
				
			||||||
                "eslint": "^8.57.0",
 | 
					                "eslint": "^8.57.0",
 | 
				
			||||||
                "eslint-config-google": "^0.14.0",
 | 
					                "eslint-config-google": "^0.14.0",
 | 
				
			||||||
                "eslint-plugin-sonarjs": "^0.25.1",
 | 
					                "eslint-plugin-sonarjs": "^0.25.1",
 | 
				
			||||||
                "npm-run-all": "^4.1.5",
 | 
					                "npm-run-all": "^4.1.5",
 | 
				
			||||||
                "prettier": "^3.3.2",
 | 
					                "prettier": "^3.3.2",
 | 
				
			||||||
                "ts-node": "^10.9.2",
 | 
					                "ts-node": "^10.9.2",
 | 
				
			||||||
                "typescript": "^5.4.5",
 | 
					                "typescript": "^5.5.3",
 | 
				
			||||||
                "wdio-wait-for": "^3.0.11"
 | 
					                "wdio-wait-for": "^3.0.11"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
@ -1189,19 +1189,19 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/cli": {
 | 
					        "node_modules/@wdio/cli": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-8.39.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-p9y6jxmpmw53OoB9v/uTLwMetmz7Q0K7NewdVONgmeTY/ERpkU15qL3fMw1rXb+E+vrV8dlce4srnXroec6SFA==",
 | 
					            "integrity": "sha512-CUze/nbOMzgSEp+Qo27dnM5IhlOPAiBJCPX3xO85/kjweqqxmAB1QBKww1Mz9YlNIXineaHrkgqlUQIvEqOJdQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.1",
 | 
					                "@types/node": "^20.1.1",
 | 
				
			||||||
                "@vitest/snapshot": "^1.2.1",
 | 
					                "@vitest/snapshot": "^1.2.1",
 | 
				
			||||||
                "@wdio/config": "8.38.2",
 | 
					                "@wdio/config": "8.39.0",
 | 
				
			||||||
                "@wdio/globals": "8.38.2",
 | 
					                "@wdio/globals": "8.39.1",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/protocols": "8.38.0",
 | 
					                "@wdio/protocols": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "async-exit-hook": "^2.0.1",
 | 
					                "async-exit-hook": "^2.0.1",
 | 
				
			||||||
                "chalk": "^5.2.0",
 | 
					                "chalk": "^5.2.0",
 | 
				
			||||||
                "chokidar": "^3.5.3",
 | 
					                "chokidar": "^3.5.3",
 | 
				
			||||||
@ -1216,7 +1216,7 @@
 | 
				
			|||||||
                "lodash.union": "^4.6.0",
 | 
					                "lodash.union": "^4.6.0",
 | 
				
			||||||
                "read-pkg-up": "10.0.0",
 | 
					                "read-pkg-up": "10.0.0",
 | 
				
			||||||
                "recursive-readdir": "^2.2.3",
 | 
					                "recursive-readdir": "^2.2.3",
 | 
				
			||||||
                "webdriverio": "8.38.2",
 | 
					                "webdriverio": "8.39.1",
 | 
				
			||||||
                "yargs": "^17.7.2"
 | 
					                "yargs": "^17.7.2"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "bin": {
 | 
					            "bin": {
 | 
				
			||||||
@ -1239,14 +1239,14 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/config": {
 | 
					        "node_modules/@wdio/config": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-xlnapTr1vOA0h5HsHTIqj47729FbG3WjxmgHweDEQvcT4C1g9l+WKf+N3FM7DNNoIsAqxKi6rOHG02rJADQJtw==",
 | 
					            "integrity": "sha512-yNuGPMPibY91s936gnJCHWlStvIyDrwLwGfLC/NCdTin4F7HL4Gp5iJnHWkJFty1/DfFi8jjoIUBNLM8HEez+A==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "decamelize": "^6.0.0",
 | 
					                "decamelize": "^6.0.0",
 | 
				
			||||||
                "deepmerge-ts": "^5.0.0",
 | 
					                "deepmerge-ts": "^5.0.0",
 | 
				
			||||||
                "glob": "^10.2.2",
 | 
					                "glob": "^10.2.2",
 | 
				
			||||||
@ -1257,29 +1257,29 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/globals": {
 | 
					        "node_modules/@wdio/globals": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-8.39.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-iIrUF1EODfHLh3V/CSNCqbNNxUTe3ND+c86zDjzJcPFjawLX1plvAApsU/eDmtsFShcOS2KHbfSjiydFoqQG1Q==",
 | 
					            "integrity": "sha512-kNb1TlxI8Le/tsOiw7CMQcG0+ZGyxk9ZDO/PQLxkJvjo/q2QmiBcgaNMPuf+j1ABETcQK4bI7QtiT5uZ+f2AGA==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
                "node": "^16.13 || >=18"
 | 
					                "node": "^16.13 || >=18"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "optionalDependencies": {
 | 
					            "optionalDependencies": {
 | 
				
			||||||
                "expect-webdriverio": "^4.11.2",
 | 
					                "expect-webdriverio": "^4.11.2",
 | 
				
			||||||
                "webdriverio": "8.38.2"
 | 
					                "webdriverio": "8.39.1"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/local-runner": {
 | 
					        "node_modules/@wdio/local-runner": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/local-runner/-/local-runner-8.39.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-syW+R5VUHJ3GBkQGFcNYe6MYwWRgklc9W7A83xQDTvKWFNHCetLvc8AtKZ54vs8MItBejjU+Oh94ZNbNX1pBcg==",
 | 
					            "integrity": "sha512-VYRD7pSkl5JTsYXroM65yb+vJVn9pFJf0XZMB7Xj+WVUqGXuVkZ+XybsQetUlhruXvHIsPdiFj12V1tMyaUHrQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.0",
 | 
					                "@types/node": "^20.1.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/repl": "8.24.12",
 | 
					                "@wdio/repl": "8.24.12",
 | 
				
			||||||
                "@wdio/runner": "8.38.2",
 | 
					                "@wdio/runner": "8.39.1",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "async-exit-hook": "^2.0.1",
 | 
					                "async-exit-hook": "^2.0.1",
 | 
				
			||||||
                "split2": "^4.1.0",
 | 
					                "split2": "^4.1.0",
 | 
				
			||||||
                "stream-buffers": "^3.0.2"
 | 
					                "stream-buffers": "^3.0.2"
 | 
				
			||||||
@ -1316,16 +1316,16 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/mocha-framework": {
 | 
					        "node_modules/@wdio/mocha-framework": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/mocha-framework/-/mocha-framework-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-qJmRL5E6/ypjCUACH4hvCAAmTdU4YUrUlp9o/IKvTIAHMnZPE0/HgUFixCeu8Mop+rdzTPVBrbqxpRDdSnraYA==",
 | 
					            "integrity": "sha512-OFau1dd5mUAqC70gkx0WeZ8rJG191Snb4qhOTS18FpszUoZgoHtgjFICC0cxqZBFtmT9j7+22hNrj6d4sQVPJw==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/mocha": "^10.0.0",
 | 
					                "@types/mocha": "^10.0.0",
 | 
				
			||||||
                "@types/node": "^20.1.0",
 | 
					                "@types/node": "^20.1.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "mocha": "^10.0.0"
 | 
					                "mocha": "^10.0.0"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
@ -1351,14 +1351,14 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/reporter": {
 | 
					        "node_modules/@wdio/reporter": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-R78UdAtAnkaV22NYlCCcbPPhmYweiDURiw64LYhlVIQrKNuXUQcafR2kRlWKy31rZc9thSLs5LzrZDReENUlFQ==",
 | 
					            "integrity": "sha512-XahXhmaA1okdwg4/ThHFSqy/41KywxhbtszPcTzyXB+9INaqFNHA1b1vvWs0mrD5+tTtKbg4caTcEHVJU4iv0w==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.0",
 | 
					                "@types/node": "^20.1.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "diff": "^5.0.0",
 | 
					                "diff": "^5.0.0",
 | 
				
			||||||
                "object-inspect": "^1.12.0"
 | 
					                "object-inspect": "^1.12.0"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -1367,35 +1367,35 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/runner": {
 | 
					        "node_modules/@wdio/runner": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/runner/-/runner-8.39.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-5lPnKSX2BBLI2AbYW+hoGPiEUAJXj8F8I6NC2LaBVzf1CLN+w2HWZ7lUiqS14XT0b5/hlSUX6+JYwUXlDbpuuw==",
 | 
					            "integrity": "sha512-hCGI+TSAyb14UtdDjswI5AAdW1CZMi6di+rDZ6ml43hQyHc6sw+74CXI8dwoJ29dcTzbg7QCJZZXV1qMn0kh2w==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.11.28",
 | 
					                "@types/node": "^20.11.28",
 | 
				
			||||||
                "@wdio/config": "8.38.2",
 | 
					                "@wdio/config": "8.39.0",
 | 
				
			||||||
                "@wdio/globals": "8.38.2",
 | 
					                "@wdio/globals": "8.39.1",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "deepmerge-ts": "^5.1.0",
 | 
					                "deepmerge-ts": "^5.1.0",
 | 
				
			||||||
                "expect-webdriverio": "^4.12.0",
 | 
					                "expect-webdriverio": "^4.12.0",
 | 
				
			||||||
                "gaze": "^1.1.3",
 | 
					                "gaze": "^1.1.3",
 | 
				
			||||||
                "webdriver": "8.38.2",
 | 
					                "webdriver": "8.39.0",
 | 
				
			||||||
                "webdriverio": "8.38.2"
 | 
					                "webdriverio": "8.39.1"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
                "node": "^16.13 || >=18"
 | 
					                "node": "^16.13 || >=18"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/spec-reporter": {
 | 
					        "node_modules/@wdio/spec-reporter": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/spec-reporter/-/spec-reporter-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-Dntk+lmrp+0I3HRRWkkXED+smshvgsW5cdLKwJhEJ1liI48MdBpdNGf9IHTVckE6nfxcWDyFI4icD9qYv/5bFA==",
 | 
					            "integrity": "sha512-2DX0+xvP+PyeVTBd6iGCH/RU66WXaa8HL+HpsJXZu5rSkZ4+6B2Tv8JB3ZE/pOWGNpI+B4ac/NfDs1DrX9sB7A==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@wdio/reporter": "8.38.2",
 | 
					                "@wdio/reporter": "8.39.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "chalk": "^5.1.2",
 | 
					                "chalk": "^5.1.2",
 | 
				
			||||||
                "easy-table": "^1.2.0",
 | 
					                "easy-table": "^1.2.0",
 | 
				
			||||||
                "pretty-ms": "^7.0.0"
 | 
					                "pretty-ms": "^7.0.0"
 | 
				
			||||||
@ -1417,9 +1417,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/types": {
 | 
					        "node_modules/@wdio/types": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-+wj1c1OSLdnN4WO5b44Ih4263dTl/eSwMGSI4/pCgIyXIuYQH38JQ+6WRa+c8vJEskUzboq2cSgEQumVZ39ozQ==",
 | 
					            "integrity": "sha512-86lcYROTapOJuFd9ouomFDfzDnv3Kn+jE0RmqfvN9frZAeLVJ5IKjX9M6HjplsyTZhjGO1uCaehmzx+HJus33Q==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.0"
 | 
					                "@types/node": "^20.1.0"
 | 
				
			||||||
@ -1429,14 +1429,14 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/@wdio/utils": {
 | 
					        "node_modules/@wdio/utils": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-y5AnBwsGcu/XuCBGCgKmlvKdwEIFyzLA+Cr+denySxY3jbWDONtPUcGaVdFALwsIa5jcIjcATqGmZcCPGnkd7g==",
 | 
					            "integrity": "sha512-jY+n6jlGeK+9Tx8T659PKLwMQTGpLW5H78CSEWgZLbjbVSr2LfGR8Lx0CRktNXxAtqEVZPj16Pi74OtAhvhE6Q==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@puppeteer/browsers": "^1.6.0",
 | 
					                "@puppeteer/browsers": "^1.6.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "decamelize": "^6.0.0",
 | 
					                "decamelize": "^6.0.0",
 | 
				
			||||||
                "deepmerge-ts": "^5.1.0",
 | 
					                "deepmerge-ts": "^5.1.0",
 | 
				
			||||||
                "edgedriver": "^5.5.0",
 | 
					                "edgedriver": "^5.5.0",
 | 
				
			||||||
@ -2084,9 +2084,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/chromedriver": {
 | 
					        "node_modules/chromedriver": {
 | 
				
			||||||
            "version": "126.0.1",
 | 
					            "version": "126.0.4",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.1.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.4.tgz",
 | 
				
			||||||
            "integrity": "sha512-uV4xIvpPimKmV1/Hn9oX0C57tjsBbVtOeKFX9JzzPYpsu8xiSUAdqa3A8mgZE5DMJKDDoDyDIrA656QncVWRzg==",
 | 
					            "integrity": "sha512-mIdJqdocfN/y9fl5BymIzM9WQLy64x078i5tS1jGFzbFAwXwXrj3zmA86Wf3R/hywPYpWqwXxFGBJHgqZTuGCA==",
 | 
				
			||||||
            "hasInstallScript": true,
 | 
					            "hasInstallScript": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@testim/chrome-version": "^1.1.4",
 | 
					                "@testim/chrome-version": "^1.1.4",
 | 
				
			||||||
@ -8703,9 +8703,9 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/typescript": {
 | 
					        "node_modules/typescript": {
 | 
				
			||||||
            "version": "5.4.5",
 | 
					            "version": "5.5.3",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
 | 
				
			||||||
            "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
 | 
					            "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "bin": {
 | 
					            "bin": {
 | 
				
			||||||
                "tsc": "bin/tsc",
 | 
					                "tsc": "bin/tsc",
 | 
				
			||||||
@ -8969,18 +8969,18 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/webdriver": {
 | 
					        "node_modules/webdriver": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.0",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.39.0.tgz",
 | 
				
			||||||
            "integrity": "sha512-NGfjW0BDYwFgOIzeojOcWGn3tYloQdvHr+Y2xKKYVqa9Rs0x1mzlTjU1kWtC4DaV8DltskwaPa7o+s8hTNpuyA==",
 | 
					            "integrity": "sha512-Kc3+SfiH4ufyrIht683VT2vnJocx0pfH8rYdyPvEh1b2OYewtFTHK36k9rBDHZiBmk6jcSXs4M2xeFgOuon9Lg==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.0",
 | 
					                "@types/node": "^20.1.0",
 | 
				
			||||||
                "@types/ws": "^8.5.3",
 | 
					                "@types/ws": "^8.5.3",
 | 
				
			||||||
                "@wdio/config": "8.38.2",
 | 
					                "@wdio/config": "8.39.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/protocols": "8.38.0",
 | 
					                "@wdio/protocols": "8.38.0",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "deepmerge-ts": "^5.1.0",
 | 
					                "deepmerge-ts": "^5.1.0",
 | 
				
			||||||
                "got": "^12.6.1",
 | 
					                "got": "^12.6.1",
 | 
				
			||||||
                "ky": "^0.33.0",
 | 
					                "ky": "^0.33.0",
 | 
				
			||||||
@ -8991,18 +8991,18 @@
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "node_modules/webdriverio": {
 | 
					        "node_modules/webdriverio": {
 | 
				
			||||||
            "version": "8.38.2",
 | 
					            "version": "8.39.1",
 | 
				
			||||||
            "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.38.2.tgz",
 | 
					            "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.39.1.tgz",
 | 
				
			||||||
            "integrity": "sha512-r09y5UfivyYh5JOzT2SpJJ1zDmQl/R4OTH12opUqkjvp21BibCQm/uu1mrxGy4lzSHljrvqSVrrcGI+6UA1O8w==",
 | 
					            "integrity": "sha512-dPwLgLNtP+l4vnybz+YFxxH8nBKOP7j6VVzKtfDyTLDQg9rz3U8OA4xMMQCBucnrVXy3KcKxGqlnMa+c4IfWCQ==",
 | 
				
			||||||
            "dev": true,
 | 
					            "dev": true,
 | 
				
			||||||
            "dependencies": {
 | 
					            "dependencies": {
 | 
				
			||||||
                "@types/node": "^20.1.0",
 | 
					                "@types/node": "^20.1.0",
 | 
				
			||||||
                "@wdio/config": "8.38.2",
 | 
					                "@wdio/config": "8.39.0",
 | 
				
			||||||
                "@wdio/logger": "8.38.0",
 | 
					                "@wdio/logger": "8.38.0",
 | 
				
			||||||
                "@wdio/protocols": "8.38.0",
 | 
					                "@wdio/protocols": "8.38.0",
 | 
				
			||||||
                "@wdio/repl": "8.24.12",
 | 
					                "@wdio/repl": "8.24.12",
 | 
				
			||||||
                "@wdio/types": "8.38.2",
 | 
					                "@wdio/types": "8.39.0",
 | 
				
			||||||
                "@wdio/utils": "8.38.2",
 | 
					                "@wdio/utils": "8.39.0",
 | 
				
			||||||
                "archiver": "^7.0.0",
 | 
					                "archiver": "^7.0.0",
 | 
				
			||||||
                "aria-query": "^5.0.0",
 | 
					                "aria-query": "^5.0.0",
 | 
				
			||||||
                "css-shorthand-properties": "^1.1.1",
 | 
					                "css-shorthand-properties": "^1.1.1",
 | 
				
			||||||
@ -9020,7 +9020,7 @@
 | 
				
			|||||||
                "resq": "^1.9.1",
 | 
					                "resq": "^1.9.1",
 | 
				
			||||||
                "rgb2hex": "0.2.5",
 | 
					                "rgb2hex": "0.2.5",
 | 
				
			||||||
                "serialize-error": "^11.0.1",
 | 
					                "serialize-error": "^11.0.1",
 | 
				
			||||||
                "webdriver": "8.38.2"
 | 
					                "webdriver": "8.39.0"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "engines": {
 | 
					            "engines": {
 | 
				
			||||||
                "node": "^16.13 || >=18"
 | 
					                "node": "^16.13 || >=18"
 | 
				
			||||||
 | 
				
			|||||||
@ -6,17 +6,17 @@
 | 
				
			|||||||
        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
					        "@trivago/prettier-plugin-sort-imports": "^4.3.0",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^7.5.0",
 | 
					        "@typescript-eslint/eslint-plugin": "^7.5.0",
 | 
				
			||||||
        "@typescript-eslint/parser": "^7.5.0",
 | 
					        "@typescript-eslint/parser": "^7.5.0",
 | 
				
			||||||
        "@wdio/cli": "^8.38.2",
 | 
					        "@wdio/cli": "^8.39.1",
 | 
				
			||||||
        "@wdio/local-runner": "^8.38.2",
 | 
					        "@wdio/local-runner": "^8.39.1",
 | 
				
			||||||
        "@wdio/mocha-framework": "^8.38.2",
 | 
					        "@wdio/mocha-framework": "^8.39.0",
 | 
				
			||||||
        "@wdio/spec-reporter": "^8.38.2",
 | 
					        "@wdio/spec-reporter": "^8.39.0",
 | 
				
			||||||
        "eslint": "^8.57.0",
 | 
					        "eslint": "^8.57.0",
 | 
				
			||||||
        "eslint-config-google": "^0.14.0",
 | 
					        "eslint-config-google": "^0.14.0",
 | 
				
			||||||
        "eslint-plugin-sonarjs": "^0.25.1",
 | 
					        "eslint-plugin-sonarjs": "^0.25.1",
 | 
				
			||||||
        "npm-run-all": "^4.1.5",
 | 
					        "npm-run-all": "^4.1.5",
 | 
				
			||||||
        "prettier": "^3.3.2",
 | 
					        "prettier": "^3.3.2",
 | 
				
			||||||
        "ts-node": "^10.9.2",
 | 
					        "ts-node": "^10.9.2",
 | 
				
			||||||
        "typescript": "^5.4.5",
 | 
					        "typescript": "^5.5.3",
 | 
				
			||||||
        "wdio-wait-for": "^3.0.11"
 | 
					        "wdio-wait-for": "^3.0.11"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "scripts": {
 | 
					    "scripts": {
 | 
				
			||||||
@ -32,6 +32,6 @@
 | 
				
			|||||||
        "node": ">=20"
 | 
					        "node": ">=20"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "chromedriver": "^126.0.1"
 | 
					        "chromedriver": "^126.0.4"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12450
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12450
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -15,10 +15,12 @@
 | 
				
			|||||||
        "build-proxy": "run-s build-locales esbuild:build-proxy",
 | 
					        "build-proxy": "run-s build-locales esbuild:build-proxy",
 | 
				
			||||||
        "watch": "run-s build-locales esbuild:watch",
 | 
					        "watch": "run-s build-locales esbuild:watch",
 | 
				
			||||||
        "lint": "cross-env NODE_OPTIONS='--max_old_space_size=65536' eslint . --max-warnings 0 --fix",
 | 
					        "lint": "cross-env NODE_OPTIONS='--max_old_space_size=65536' eslint . --max-warnings 0 --fix",
 | 
				
			||||||
 | 
					        "lint:lockfile": "lockfile-lint --path package.json --type npm --allowed-hosts npm --validate-https",
 | 
				
			||||||
        "lint:precommit": "bun scripts/eslint-precommit.mjs",
 | 
					        "lint:precommit": "bun scripts/eslint-precommit.mjs",
 | 
				
			||||||
        "lint:spelling": "node scripts/check-spelling.mjs",
 | 
					        "lint:spelling": "node scripts/check-spelling.mjs",
 | 
				
			||||||
        "lit-analyse": "lit-analyzer src",
 | 
					        "lit-analyse": "lit-analyzer src",
 | 
				
			||||||
        "precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling --sequential lint:precommit prettier",
 | 
					        "postinstall": "bash scripts/patch-spotlight.sh",
 | 
				
			||||||
 | 
					        "precommit": "npm-run-all --parallel tsc lit-analyse lint:spelling lint:lockfile --sequential lint:precommit prettier",
 | 
				
			||||||
        "prequick": "run-s tsc:execute lit-analyse lint:precommit lint:spelling",
 | 
					        "prequick": "run-s tsc:execute lit-analyse lint:precommit lint:spelling",
 | 
				
			||||||
        "prettier-check": "prettier --check .",
 | 
					        "prettier-check": "prettier --check .",
 | 
				
			||||||
        "prettier": "prettier --write .",
 | 
					        "prettier": "prettier --write .",
 | 
				
			||||||
@ -27,7 +29,10 @@
 | 
				
			|||||||
        "tsc": "run-s build-locales tsc:execute",
 | 
					        "tsc": "run-s build-locales tsc:execute",
 | 
				
			||||||
        "storybook": "storybook dev -p 6006",
 | 
					        "storybook": "storybook dev -p 6006",
 | 
				
			||||||
        "storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=8192' storybook build",
 | 
					        "storybook:build": "cross-env NODE_OPTIONS='--max_old_space_size=8192' storybook build",
 | 
				
			||||||
        "storybook:build-import-map": "node scripts/build-storybook-import-maps.mjs"
 | 
					        "storybook:build-import-map": "node scripts/build-storybook-import-maps.mjs",
 | 
				
			||||||
 | 
					        "test": "npx wdio run ./wdio.conf.ts --logLevel=warn --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json",
 | 
				
			||||||
 | 
					        "test-view": "npx wdio run ./wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json",
 | 
				
			||||||
 | 
					        "test-watch": "npx wdio run ./wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json --watch"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "dependencies": {
 | 
					    "dependencies": {
 | 
				
			||||||
        "@codemirror/lang-html": "^6.4.9",
 | 
					        "@codemirror/lang-html": "^6.4.9",
 | 
				
			||||||
@ -38,7 +43,7 @@
 | 
				
			|||||||
        "@codemirror/theme-one-dark": "^6.1.2",
 | 
					        "@codemirror/theme-one-dark": "^6.1.2",
 | 
				
			||||||
        "@formatjs/intl-listformat": "^7.5.7",
 | 
					        "@formatjs/intl-listformat": "^7.5.7",
 | 
				
			||||||
        "@fortawesome/fontawesome-free": "^6.5.2",
 | 
					        "@fortawesome/fontawesome-free": "^6.5.2",
 | 
				
			||||||
        "@goauthentik/api": "^2024.4.2-1718378698",
 | 
					        "@goauthentik/api": "^2024.6.0-1720200294",
 | 
				
			||||||
        "@lit/context": "^1.1.2",
 | 
					        "@lit/context": "^1.1.2",
 | 
				
			||||||
        "@lit/localize": "^0.12.1",
 | 
					        "@lit/localize": "^0.12.1",
 | 
				
			||||||
        "@lit/reactive-element": "^2.0.4",
 | 
					        "@lit/reactive-element": "^2.0.4",
 | 
				
			||||||
@ -46,7 +51,7 @@
 | 
				
			|||||||
        "@open-wc/lit-helpers": "^0.7.0",
 | 
					        "@open-wc/lit-helpers": "^0.7.0",
 | 
				
			||||||
        "@patternfly/elements": "^3.0.2",
 | 
					        "@patternfly/elements": "^3.0.2",
 | 
				
			||||||
        "@patternfly/patternfly": "^4.224.2",
 | 
					        "@patternfly/patternfly": "^4.224.2",
 | 
				
			||||||
        "@sentry/browser": "^8.9.2",
 | 
					        "@sentry/browser": "^8.15.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.3",
 | 
					        "chart.js": "^4.4.3",
 | 
				
			||||||
@ -82,26 +87,30 @@
 | 
				
			|||||||
        "@lit/localize-tools": "^0.7.2",
 | 
					        "@lit/localize-tools": "^0.7.2",
 | 
				
			||||||
        "@rollup/plugin-replace": "^5.0.7",
 | 
					        "@rollup/plugin-replace": "^5.0.7",
 | 
				
			||||||
        "@spotlightjs/spotlight": "^2.0.0",
 | 
					        "@spotlightjs/spotlight": "^2.0.0",
 | 
				
			||||||
        "@storybook/addon-essentials": "^8.1.10",
 | 
					        "@storybook/addon-essentials": "^8.1.11",
 | 
				
			||||||
        "@storybook/addon-links": "^8.1.10",
 | 
					        "@storybook/addon-links": "^8.1.11",
 | 
				
			||||||
        "@storybook/api": "^7.6.17",
 | 
					        "@storybook/api": "^7.6.17",
 | 
				
			||||||
        "@storybook/blocks": "^8.0.8",
 | 
					        "@storybook/blocks": "^8.0.8",
 | 
				
			||||||
        "@storybook/manager-api": "^8.1.10",
 | 
					        "@storybook/manager-api": "^8.1.11",
 | 
				
			||||||
        "@storybook/web-components": "^8.1.10",
 | 
					        "@storybook/web-components": "^8.1.11",
 | 
				
			||||||
        "@storybook/web-components-vite": "^8.1.10",
 | 
					        "@storybook/web-components-vite": "^8.1.11",
 | 
				
			||||||
        "@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/grecaptcha": "^3.0.9",
 | 
					        "@types/grecaptcha": "^3.0.9",
 | 
				
			||||||
        "@types/guacamole-common-js": "1.5.2",
 | 
					        "@types/guacamole-common-js": "1.5.2",
 | 
				
			||||||
        "@types/showdown": "^2.0.6",
 | 
					        "@types/showdown": "^2.0.6",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^7.5.0",
 | 
					        "@typescript-eslint/eslint-plugin": "^7.14.0",
 | 
				
			||||||
        "@typescript-eslint/parser": "^7.5.0",
 | 
					        "@typescript-eslint/parser": "^7.14.0",
 | 
				
			||||||
 | 
					        "@wdio/browser-runner": "^8.39.1",
 | 
				
			||||||
 | 
					        "@wdio/cli": "^8.39.1",
 | 
				
			||||||
 | 
					        "@wdio/mocha-framework": "^8.36.1",
 | 
				
			||||||
 | 
					        "@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": "^3.6.0",
 | 
					        "chokidar": "^3.6.0",
 | 
				
			||||||
        "cross-env": "^7.0.3",
 | 
					        "cross-env": "^7.0.3",
 | 
				
			||||||
        "esbuild": "^0.21.5",
 | 
					        "esbuild": "^0.23.0",
 | 
				
			||||||
        "eslint": "^8.57.0",
 | 
					        "eslint": "^8.57.0",
 | 
				
			||||||
        "eslint-config-google": "^0.14.0",
 | 
					        "eslint-config-google": "^0.14.0",
 | 
				
			||||||
        "eslint-plugin-custom-elements": "0.0.8",
 | 
					        "eslint-plugin-custom-elements": "0.0.8",
 | 
				
			||||||
@ -109,8 +118,9 @@
 | 
				
			|||||||
        "eslint-plugin-sonarjs": "^0.25.1",
 | 
					        "eslint-plugin-sonarjs": "^0.25.1",
 | 
				
			||||||
        "eslint-plugin-storybook": "^0.8.0",
 | 
					        "eslint-plugin-storybook": "^0.8.0",
 | 
				
			||||||
        "github-slugger": "^2.0.0",
 | 
					        "github-slugger": "^2.0.0",
 | 
				
			||||||
        "glob": "^10.4.1",
 | 
					        "glob": "^10.4.3",
 | 
				
			||||||
        "lit-analyzer": "^2.0.3",
 | 
					        "lit-analyzer": "^2.0.3",
 | 
				
			||||||
 | 
					        "lockfile-lint": "^4.14.0",
 | 
				
			||||||
        "npm-run-all": "^4.1.5",
 | 
					        "npm-run-all": "^4.1.5",
 | 
				
			||||||
        "prettier": "^3.3.2",
 | 
					        "prettier": "^3.3.2",
 | 
				
			||||||
        "pseudolocale": "^2.0.0",
 | 
					        "pseudolocale": "^2.0.0",
 | 
				
			||||||
@ -118,18 +128,20 @@
 | 
				
			|||||||
        "react-dom": "^18.3.1",
 | 
					        "react-dom": "^18.3.1",
 | 
				
			||||||
        "rollup-plugin-modify": "^3.0.0",
 | 
					        "rollup-plugin-modify": "^3.0.0",
 | 
				
			||||||
        "rollup-plugin-postcss-lit": "^2.1.0",
 | 
					        "rollup-plugin-postcss-lit": "^2.1.0",
 | 
				
			||||||
        "storybook": "^8.1.10",
 | 
					        "storybook": "^8.1.11",
 | 
				
			||||||
        "storybook-addon-mock": "^5.0.0",
 | 
					        "storybook-addon-mock": "^5.0.0",
 | 
				
			||||||
        "ts-lit-plugin": "^2.0.2",
 | 
					        "ts-lit-plugin": "^2.0.2",
 | 
				
			||||||
 | 
					        "ts-node": "^10.9.2",
 | 
				
			||||||
        "tslib": "^2.6.3",
 | 
					        "tslib": "^2.6.3",
 | 
				
			||||||
        "turnstile-types": "^1.2.1",
 | 
					        "turnstile-types": "^1.2.1",
 | 
				
			||||||
        "typescript": "^5.4.5",
 | 
					        "typescript": "^5.5.3",
 | 
				
			||||||
        "vite-tsconfig-paths": "^4.3.2"
 | 
					        "vite-tsconfig-paths": "^4.3.2",
 | 
				
			||||||
 | 
					        "wdio-wait-for": "^3.0.11"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "optionalDependencies": {
 | 
					    "optionalDependencies": {
 | 
				
			||||||
        "@esbuild/darwin-arm64": "^0.21.4",
 | 
					        "@esbuild/darwin-arm64": "^0.23.0",
 | 
				
			||||||
        "@esbuild/linux-amd64": "^0.18.11",
 | 
					        "@esbuild/linux-amd64": "^0.18.11",
 | 
				
			||||||
        "@esbuild/linux-arm64": "^0.21.4",
 | 
					        "@esbuild/linux-arm64": "^0.23.0",
 | 
				
			||||||
        "@rollup/rollup-darwin-arm64": "4.18.0",
 | 
					        "@rollup/rollup-darwin-arm64": "4.18.0",
 | 
				
			||||||
        "@rollup/rollup-linux-arm64-gnu": "4.18.0",
 | 
					        "@rollup/rollup-linux-arm64-gnu": "4.18.0",
 | 
				
			||||||
        "@rollup/rollup-linux-x64-gnu": "4.18.0"
 | 
					        "@rollup/rollup-linux-x64-gnu": "4.18.0"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								web/scripts/patch-spotlight.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								web/scripts/patch-spotlight.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TARGET=$(find "./node_modules/@spotlightjs/overlay/dist/" -name "index-[0-9a-f]*.js");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ! grep -GL 'QX2 = ' "$TARGET" > /dev/null ; then
 | 
				
			||||||
 | 
					patch --forward --no-backup-if-mismatch -p0 "$TARGET" <<EOF
 | 
				
			||||||
 | 
					--- a/index-5682ce90.js	2024-06-13 16:19:28
 | 
				
			||||||
 | 
					+++ b/index-5682ce90.js	2024-06-13 16:20:23
 | 
				
			||||||
 | 
					@@ -4958,11 +4958,10 @@
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					   );
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					-const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m));
 | 
				
			||||||
 | 
					+const q2 = w.lazy(() => import("./main-3257b7fc.js").then((n) => n.m)), QX2 = () => {};
 | 
				
			||||||
 | 
					 function Gp({
 | 
				
			||||||
 | 
					   data: n,
 | 
				
			||||||
 | 
					-  onUpdateData: a = () => {
 | 
				
			||||||
 | 
					-  },
 | 
				
			||||||
 | 
					+  onUpdateData: a = QX2,
 | 
				
			||||||
 | 
					   editingEnabled: s = !1,
 | 
				
			||||||
 | 
					   clipboardEnabled: o = !1,
 | 
				
			||||||
 | 
					   displayDataTypes: c = !1,
 | 
				
			||||||
 | 
					EOF
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    echo "spotlight overlay.js patch already applied"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
							
								
								
									
										529
									
								
								web/sfe/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										529
									
								
								web/sfe/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,529 @@
 | 
				
			|||||||
 | 
					import { fromByteArray } from "base64-js";
 | 
				
			||||||
 | 
					import "formdata-polyfill";
 | 
				
			||||||
 | 
					import $ from "jquery";
 | 
				
			||||||
 | 
					import "weakmap-polyfill";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    type AuthenticatorValidationChallenge,
 | 
				
			||||||
 | 
					    type AutosubmitChallenge,
 | 
				
			||||||
 | 
					    type ChallengeTypes,
 | 
				
			||||||
 | 
					    ChallengeTypesFromJSON,
 | 
				
			||||||
 | 
					    type ContextualFlowInfo,
 | 
				
			||||||
 | 
					    type DeviceChallenge,
 | 
				
			||||||
 | 
					    type ErrorDetail,
 | 
				
			||||||
 | 
					    type IdentificationChallenge,
 | 
				
			||||||
 | 
					    type PasswordChallenge,
 | 
				
			||||||
 | 
					    type RedirectChallenge,
 | 
				
			||||||
 | 
					} from "@goauthentik/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface GlobalAuthentik {
 | 
				
			||||||
 | 
					    brand: {
 | 
				
			||||||
 | 
					        branding_logo: string;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ak(): GlobalAuthentik {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        window as unknown as {
 | 
				
			||||||
 | 
					            authentik: GlobalAuthentik;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ).authentik;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SimpleFlowExecutor {
 | 
				
			||||||
 | 
					    challenge?: ChallengeTypes;
 | 
				
			||||||
 | 
					    flowSlug: string;
 | 
				
			||||||
 | 
					    container: HTMLDivElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(container: HTMLDivElement) {
 | 
				
			||||||
 | 
					        this.flowSlug = window.location.pathname.split("/")[3];
 | 
				
			||||||
 | 
					        this.container = container;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get apiURL() {
 | 
				
			||||||
 | 
					        return `/api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    start() {
 | 
				
			||||||
 | 
					        $.ajax({
 | 
				
			||||||
 | 
					            type: "GET",
 | 
				
			||||||
 | 
					            url: this.apiURL,
 | 
				
			||||||
 | 
					            success: (data) => {
 | 
				
			||||||
 | 
					                this.challenge = ChallengeTypesFromJSON(data);
 | 
				
			||||||
 | 
					                this.renderChallenge();
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    submit(data: { [key: string]: unknown } | FormData) {
 | 
				
			||||||
 | 
					        $("button[type=submit]").addClass("disabled")
 | 
				
			||||||
 | 
					            .html(`<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
 | 
				
			||||||
 | 
					                <span role="status">Loading...</span>`);
 | 
				
			||||||
 | 
					        let finalData: { [key: string]: unknown } = {};
 | 
				
			||||||
 | 
					        if (data instanceof FormData) {
 | 
				
			||||||
 | 
					            finalData = {};
 | 
				
			||||||
 | 
					            data.forEach((value, key) => {
 | 
				
			||||||
 | 
					                finalData[key] = value;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            finalData = data;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $.ajax({
 | 
				
			||||||
 | 
					            type: "POST",
 | 
				
			||||||
 | 
					            url: this.apiURL,
 | 
				
			||||||
 | 
					            data: JSON.stringify(finalData),
 | 
				
			||||||
 | 
					            success: (data) => {
 | 
				
			||||||
 | 
					                this.challenge = ChallengeTypesFromJSON(data);
 | 
				
			||||||
 | 
					                this.renderChallenge();
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            contentType: "application/json",
 | 
				
			||||||
 | 
					            dataType: "json",
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderChallenge() {
 | 
				
			||||||
 | 
					        switch (this.challenge?.component) {
 | 
				
			||||||
 | 
					            case "ak-stage-identification":
 | 
				
			||||||
 | 
					                new IdentificationStage(this, this.challenge).render();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            case "ak-stage-password":
 | 
				
			||||||
 | 
					                new PasswordStage(this, this.challenge).render();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            case "xak-flow-redirect":
 | 
				
			||||||
 | 
					                new RedirectStage(this, this.challenge).render();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            case "ak-stage-autosubmit":
 | 
				
			||||||
 | 
					                new AutosubmitStage(this, this.challenge).render();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            case "ak-stage-authenticator-validate":
 | 
				
			||||||
 | 
					                new AuthenticatorValidateStage(this, this.challenge).render();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                this.container.innerText = "Unsupported stage: " + this.challenge?.component;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface FlowInfoChallenge {
 | 
				
			||||||
 | 
					    flowInfo?: ContextualFlowInfo;
 | 
				
			||||||
 | 
					    responseErrors?: {
 | 
				
			||||||
 | 
					        [key: string]: Array<ErrorDetail>;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Stage<T extends FlowInfoChallenge> {
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        public executor: SimpleFlowExecutor,
 | 
				
			||||||
 | 
					        public challenge: T,
 | 
				
			||||||
 | 
					    ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    error(fieldName: string) {
 | 
				
			||||||
 | 
					        if (!this.challenge.responseErrors) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return this.challenge.responseErrors[fieldName] || [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderInputError(fieldName: string) {
 | 
				
			||||||
 | 
					        return `${this.error(fieldName)
 | 
				
			||||||
 | 
					            .map((error) => {
 | 
				
			||||||
 | 
					                return `<div class="invalid-feedback">
 | 
				
			||||||
 | 
					                    ${error.string}
 | 
				
			||||||
 | 
					                </div>`;
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .join("")}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderNonFieldErrors() {
 | 
				
			||||||
 | 
					        return `${this.error("non_field_errors")
 | 
				
			||||||
 | 
					            .map((error) => {
 | 
				
			||||||
 | 
					                return `<div class="alert alert-danger" role="alert">
 | 
				
			||||||
 | 
					                    ${error.string}
 | 
				
			||||||
 | 
					                </div>`;
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .join("")}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    html(html: string) {
 | 
				
			||||||
 | 
					        this.executor.container.innerHTML = html;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        throw new Error("Abstract method");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IdentificationStage extends Stage<IdentificationChallenge> {
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        this.html(`
 | 
				
			||||||
 | 
					            <form id="ident-form">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                ${
 | 
				
			||||||
 | 
					                    this.challenge.applicationPre
 | 
				
			||||||
 | 
					                        ? `<p>
 | 
				
			||||||
 | 
					                              Login to continue to ${this.challenge.applicationPre}.
 | 
				
			||||||
 | 
					                          </p>`
 | 
				
			||||||
 | 
					                        : ""
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                <div class="form-label-group my-3 has-validation">
 | 
				
			||||||
 | 
					                    <input type="text" autofocus class="form-control" name="uid_field" placeholder="Email / Username">
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                ${
 | 
				
			||||||
 | 
					                    this.challenge.passwordFields
 | 
				
			||||||
 | 
					                        ? `<div class="form-label-group my-3 has-validation">
 | 
				
			||||||
 | 
					                                <input type="password" class="form-control ${this.error("password").length > 0 ? "is-invalid" : ""}" name="password" placeholder="Password">
 | 
				
			||||||
 | 
					                                ${this.renderInputError("password")}
 | 
				
			||||||
 | 
					                        </div>`
 | 
				
			||||||
 | 
					                        : ""
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ${this.renderNonFieldErrors()}
 | 
				
			||||||
 | 
					                <button class="btn btn-primary w-100 py-2" type="submit">${this.challenge.primaryAction}</button>
 | 
				
			||||||
 | 
					            </form>`);
 | 
				
			||||||
 | 
					        $("#ident-form input[name=uid_field]").trigger("focus");
 | 
				
			||||||
 | 
					        $("#ident-form").on("submit", (ev) => {
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					            const data = new FormData(ev.target as HTMLFormElement);
 | 
				
			||||||
 | 
					            this.executor.submit(data);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PasswordStage extends Stage<PasswordChallenge> {
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        this.html(`
 | 
				
			||||||
 | 
					            <form id="password-form">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                <div class="form-label-group my-3 has-validation">
 | 
				
			||||||
 | 
					                    <input type="password" autofocus class="form-control ${this.error("password").length > 0 ? "is-invalid" : ""}" name="password" placeholder="Password">
 | 
				
			||||||
 | 
					                    ${this.renderInputError("password")}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <button class="btn btn-primary w-100 py-2" type="submit">Continue</button>
 | 
				
			||||||
 | 
					            </form>`);
 | 
				
			||||||
 | 
					        $("#password-form input").trigger("focus");
 | 
				
			||||||
 | 
					        $("#password-form").on("submit", (ev) => {
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					            const data = new FormData(ev.target as HTMLFormElement);
 | 
				
			||||||
 | 
					            this.executor.submit(data);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RedirectStage extends Stage<RedirectChallenge> {
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        window.location.assign(this.challenge.to);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AutosubmitStage extends Stage<AutosubmitChallenge> {
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        this.html(`
 | 
				
			||||||
 | 
					            <form id="autosubmit-form" action="${this.challenge.url}" method="POST">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                ${Object.entries(this.challenge.attrs).map(([key, value]) => {
 | 
				
			||||||
 | 
					                    return `<input
 | 
				
			||||||
 | 
					                            type="hidden"
 | 
				
			||||||
 | 
					                            name="${key}"
 | 
				
			||||||
 | 
					                            value="${value}"
 | 
				
			||||||
 | 
					                        />`;
 | 
				
			||||||
 | 
					                })}
 | 
				
			||||||
 | 
					                <div class="d-flex justify-content-center">
 | 
				
			||||||
 | 
					                    <div class="spinner-border" role="status">
 | 
				
			||||||
 | 
					                        <span class="sr-only">Loading...</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </form>`);
 | 
				
			||||||
 | 
					        $("#autosubmit-form").submit();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Assertion {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    rawId: string;
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					    registrationClientExtensions: string;
 | 
				
			||||||
 | 
					    response: {
 | 
				
			||||||
 | 
					        clientDataJSON: string;
 | 
				
			||||||
 | 
					        attestationObject: string;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface AuthAssertion {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    rawId: string;
 | 
				
			||||||
 | 
					    type: string;
 | 
				
			||||||
 | 
					    assertionClientExtensions: string;
 | 
				
			||||||
 | 
					    response: {
 | 
				
			||||||
 | 
					        clientDataJSON: string;
 | 
				
			||||||
 | 
					        authenticatorData: string;
 | 
				
			||||||
 | 
					        signature: string;
 | 
				
			||||||
 | 
					        userHandle: string | null;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
 | 
				
			||||||
 | 
					    deviceChallenge?: DeviceChallenge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b64enc(buf: Uint8Array): string {
 | 
				
			||||||
 | 
					        return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b64RawEnc(buf: Uint8Array): string {
 | 
				
			||||||
 | 
					        return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u8arr(input: string): Uint8Array {
 | 
				
			||||||
 | 
					        return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
 | 
				
			||||||
 | 
					            c.charCodeAt(0),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    checkWebAuthnSupport(): boolean {
 | 
				
			||||||
 | 
					        if ("credentials" in navigator) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (window.location.protocol === "http:" && window.location.hostname !== "localhost") {
 | 
				
			||||||
 | 
					            console.warn("WebAuthn requires this page to be accessed via HTTPS.");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        console.warn("WebAuthn not supported by browser.");
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Transforms items in the credentialCreateOptions generated on the server
 | 
				
			||||||
 | 
					     * into byte arrays expected by the navigator.credentials.create() call
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    transformCredentialCreateOptions(
 | 
				
			||||||
 | 
					        credentialCreateOptions: PublicKeyCredentialCreationOptions,
 | 
				
			||||||
 | 
					        userId: string,
 | 
				
			||||||
 | 
					    ): PublicKeyCredentialCreationOptions {
 | 
				
			||||||
 | 
					        const user = credentialCreateOptions.user;
 | 
				
			||||||
 | 
					        // Because json can't contain raw bytes, the server base64-encodes the User ID
 | 
				
			||||||
 | 
					        // So to get the base64 encoded byte array, we first need to convert it to a regular
 | 
				
			||||||
 | 
					        // string, then a byte array, re-encode it and wrap that in an array.
 | 
				
			||||||
 | 
					        const stringId = decodeURIComponent(window.atob(userId));
 | 
				
			||||||
 | 
					        user.id = this.u8arr(this.b64enc(this.u8arr(stringId)));
 | 
				
			||||||
 | 
					        const challenge = this.u8arr(credentialCreateOptions.challenge.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const transformedCredentialCreateOptions = Object.assign({}, credentialCreateOptions, {
 | 
				
			||||||
 | 
					            challenge,
 | 
				
			||||||
 | 
					            user,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return transformedCredentialCreateOptions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Transforms the binary data in the credential into base64 strings
 | 
				
			||||||
 | 
					     * for posting to the server.
 | 
				
			||||||
 | 
					     * @param {PublicKeyCredential} newAssertion
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
 | 
				
			||||||
 | 
					        const attObj = new Uint8Array(
 | 
				
			||||||
 | 
					            (newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
 | 
				
			||||||
 | 
					        const rawId = new Uint8Array(newAssertion.rawId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const registrationClientExtensions = newAssertion.getClientExtensionResults();
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            id: newAssertion.id,
 | 
				
			||||||
 | 
					            rawId: this.b64enc(rawId),
 | 
				
			||||||
 | 
					            type: newAssertion.type,
 | 
				
			||||||
 | 
					            registrationClientExtensions: JSON.stringify(registrationClientExtensions),
 | 
				
			||||||
 | 
					            response: {
 | 
				
			||||||
 | 
					                clientDataJSON: this.b64enc(clientDataJSON),
 | 
				
			||||||
 | 
					                attestationObject: this.b64enc(attObj),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    transformCredentialRequestOptions(
 | 
				
			||||||
 | 
					        credentialRequestOptions: PublicKeyCredentialRequestOptions,
 | 
				
			||||||
 | 
					    ): PublicKeyCredentialRequestOptions {
 | 
				
			||||||
 | 
					        const challenge = this.u8arr(credentialRequestOptions.challenge.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
 | 
				
			||||||
 | 
					            (credentialDescriptor) => {
 | 
				
			||||||
 | 
					                const id = this.u8arr(credentialDescriptor.id.toString());
 | 
				
			||||||
 | 
					                return Object.assign({}, credentialDescriptor, { id });
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const transformedCredentialRequestOptions = Object.assign({}, credentialRequestOptions, {
 | 
				
			||||||
 | 
					            challenge,
 | 
				
			||||||
 | 
					            allowCredentials,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return transformedCredentialRequestOptions;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Encodes the binary data in the assertion into strings for posting to the server.
 | 
				
			||||||
 | 
					     * @param {PublicKeyCredential} newAssertion
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
 | 
				
			||||||
 | 
					        const response = newAssertion.response as AuthenticatorAssertionResponse;
 | 
				
			||||||
 | 
					        const authData = new Uint8Array(response.authenticatorData);
 | 
				
			||||||
 | 
					        const clientDataJSON = new Uint8Array(response.clientDataJSON);
 | 
				
			||||||
 | 
					        const rawId = new Uint8Array(newAssertion.rawId);
 | 
				
			||||||
 | 
					        const sig = new Uint8Array(response.signature);
 | 
				
			||||||
 | 
					        const assertionClientExtensions = newAssertion.getClientExtensionResults();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            id: newAssertion.id,
 | 
				
			||||||
 | 
					            rawId: this.b64enc(rawId),
 | 
				
			||||||
 | 
					            type: newAssertion.type,
 | 
				
			||||||
 | 
					            assertionClientExtensions: JSON.stringify(assertionClientExtensions),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            response: {
 | 
				
			||||||
 | 
					                clientDataJSON: this.b64RawEnc(clientDataJSON),
 | 
				
			||||||
 | 
					                signature: this.b64RawEnc(sig),
 | 
				
			||||||
 | 
					                authenticatorData: this.b64RawEnc(authData),
 | 
				
			||||||
 | 
					                userHandle: null,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        if (!this.deviceChallenge) {
 | 
				
			||||||
 | 
					            return this.renderChallengePicker();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        switch (this.deviceChallenge.deviceClass) {
 | 
				
			||||||
 | 
					            case "static":
 | 
				
			||||||
 | 
					            case "totp":
 | 
				
			||||||
 | 
					                this.renderCodeInput();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case "webauthn":
 | 
				
			||||||
 | 
					                this.renderWebauthn();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderChallengePicker() {
 | 
				
			||||||
 | 
					        const challenges = this.challenge.deviceChallenges.filter((challenge) => {
 | 
				
			||||||
 | 
					            if (challenge.deviceClass === "webauthn") {
 | 
				
			||||||
 | 
					                if (!this.checkWebAuthnSupport()) {
 | 
				
			||||||
 | 
					                    return undefined;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return challenge;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.html(`<form id="picker-form">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                ${
 | 
				
			||||||
 | 
					                    challenges.length > 0
 | 
				
			||||||
 | 
					                        ? "<p>Select an authentication method.</p>"
 | 
				
			||||||
 | 
					                        : `
 | 
				
			||||||
 | 
					                    <p>No compatible authentication method available</p>
 | 
				
			||||||
 | 
					                    `
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ${challenges
 | 
				
			||||||
 | 
					                    .map((challenge) => {
 | 
				
			||||||
 | 
					                        let label = undefined;
 | 
				
			||||||
 | 
					                        switch (challenge.deviceClass) {
 | 
				
			||||||
 | 
					                            case "static":
 | 
				
			||||||
 | 
					                                label = "Recovery keys";
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                            case "totp":
 | 
				
			||||||
 | 
					                                label = "Traditional authenticator";
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                            case "webauthn":
 | 
				
			||||||
 | 
					                                label = "Security key";
 | 
				
			||||||
 | 
					                                break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if (!label) {
 | 
				
			||||||
 | 
					                            return "";
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        return `<div class="form-label-group my-3 has-validation">
 | 
				
			||||||
 | 
					                            <button id="${challenge.deviceClass}-${challenge.deviceUid}" class="btn btn-secondary w-100 py-2" type="button">
 | 
				
			||||||
 | 
					                                ${label}
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </div>`;
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    .join("")}
 | 
				
			||||||
 | 
					            </form>`);
 | 
				
			||||||
 | 
					        this.challenge.deviceChallenges.forEach((challenge) => {
 | 
				
			||||||
 | 
					            $(`#picker-form button#${challenge.deviceClass}-${challenge.deviceUid}`).on(
 | 
				
			||||||
 | 
					                "click",
 | 
				
			||||||
 | 
					                () => {
 | 
				
			||||||
 | 
					                    this.deviceChallenge = challenge;
 | 
				
			||||||
 | 
					                    this.render();
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderCodeInput() {
 | 
				
			||||||
 | 
					        this.html(`
 | 
				
			||||||
 | 
					            <form id="totp-form">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                <div class="form-label-group my-3 has-validation">
 | 
				
			||||||
 | 
					                    <input type="text" autofocus class="form-control ${this.error("code").length > 0 ? "is-invalid" : ""}" name="code" placeholder="Please enter your code" autocomplete="one-time-code">
 | 
				
			||||||
 | 
					                    ${this.renderInputError("code")}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <button class="btn btn-primary w-100 py-2" type="submit">Continue</button>
 | 
				
			||||||
 | 
					            </form>`);
 | 
				
			||||||
 | 
					        $("#totp-form input").trigger("focus");
 | 
				
			||||||
 | 
					        $("#totp-form").on("submit", (ev) => {
 | 
				
			||||||
 | 
					            ev.preventDefault();
 | 
				
			||||||
 | 
					            const data = new FormData(ev.target as HTMLFormElement);
 | 
				
			||||||
 | 
					            this.executor.submit(data);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderWebauthn() {
 | 
				
			||||||
 | 
					        this.html(`
 | 
				
			||||||
 | 
					            <form id="totp-form">
 | 
				
			||||||
 | 
					                <img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
 | 
				
			||||||
 | 
					                <h1 class="h3 mb-3 fw-normal text-center">${this.challenge?.flowInfo?.title}</h1>
 | 
				
			||||||
 | 
					                <div class="d-flex justify-content-center">
 | 
				
			||||||
 | 
					                    <div class="spinner-border" role="status">
 | 
				
			||||||
 | 
					                        <span class="sr-only">Loading...</span>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					            `);
 | 
				
			||||||
 | 
					        navigator.credentials
 | 
				
			||||||
 | 
					            .get({
 | 
				
			||||||
 | 
					                publicKey: this.transformCredentialRequestOptions(
 | 
				
			||||||
 | 
					                    this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .then((assertion) => {
 | 
				
			||||||
 | 
					                if (!assertion) {
 | 
				
			||||||
 | 
					                    throw new Error("No assertion");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // we now have an authentication assertion! encode the byte arrays contained
 | 
				
			||||||
 | 
					                    // in the assertion data as strings for posting to the server
 | 
				
			||||||
 | 
					                    const transformedAssertionForServer = this.transformAssertionForServer(
 | 
				
			||||||
 | 
					                        assertion as PublicKeyCredential,
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // post the assertion to the server for verification.
 | 
				
			||||||
 | 
					                    this.executor.submit({
 | 
				
			||||||
 | 
					                        webauthn: transformedAssertionForServer,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                } catch (err) {
 | 
				
			||||||
 | 
					                    throw new Error(`Error when validating assertion on server: ${err}`);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch((error) => {
 | 
				
			||||||
 | 
					                console.warn(error);
 | 
				
			||||||
 | 
					                this.deviceChallenge = undefined;
 | 
				
			||||||
 | 
					                this.render();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sfe = new SimpleFlowExecutor($("#flow-sfe-container")[0] as HTMLDivElement);
 | 
				
			||||||
 | 
					sfe.start();
 | 
				
			||||||
							
								
								
									
										2862
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2862
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								web/sfe/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								web/sfe/package.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "name": "@goauthentik/web-sfe",
 | 
				
			||||||
 | 
					    "version": "0.0.0",
 | 
				
			||||||
 | 
					    "private": true,
 | 
				
			||||||
 | 
					    "license": "MIT",
 | 
				
			||||||
 | 
					    "dependencies": {
 | 
				
			||||||
 | 
					        "@goauthentik/api": "^2024.6.0-1719577139",
 | 
				
			||||||
 | 
					        "base64-js": "^1.5.1",
 | 
				
			||||||
 | 
					        "bootstrap": "^4.6.1",
 | 
				
			||||||
 | 
					        "formdata-polyfill": "^4.0.10",
 | 
				
			||||||
 | 
					        "jquery": "^3.7.1",
 | 
				
			||||||
 | 
					        "weakmap-polyfill": "^2.0.4"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "scripts": {
 | 
				
			||||||
 | 
					        "build": "rollup -c rollup.config.js --bundleConfigAsCjs",
 | 
				
			||||||
 | 
					        "watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "devDependencies": {
 | 
				
			||||||
 | 
					        "@rollup/plugin-commonjs": "^26.0.1",
 | 
				
			||||||
 | 
					        "@rollup/plugin-node-resolve": "^15.2.3",
 | 
				
			||||||
 | 
					        "@rollup/plugin-swc": "^0.3.1",
 | 
				
			||||||
 | 
					        "@swc/cli": "^0.4.0",
 | 
				
			||||||
 | 
					        "@swc/core": "^1.6.13",
 | 
				
			||||||
 | 
					        "@types/jquery": "^3.5.30",
 | 
				
			||||||
 | 
					        "rollup": "^4.18.0",
 | 
				
			||||||
 | 
					        "rollup-plugin-copy": "^3.5.0"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								web/sfe/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								web/sfe/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import commonjs from "@rollup/plugin-commonjs";
 | 
				
			||||||
 | 
					import resolve from "@rollup/plugin-node-resolve";
 | 
				
			||||||
 | 
					import swc from "@rollup/plugin-swc";
 | 
				
			||||||
 | 
					import copy from "rollup-plugin-copy";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					    input: "index.ts",
 | 
				
			||||||
 | 
					    output: {
 | 
				
			||||||
 | 
					        dir: "../dist/sfe",
 | 
				
			||||||
 | 
					        format: "cjs",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    context: "window",
 | 
				
			||||||
 | 
					    plugins: [
 | 
				
			||||||
 | 
					        copy({
 | 
				
			||||||
 | 
					            targets: [
 | 
				
			||||||
 | 
					                { src: "node_modules/bootstrap/dist/css/bootstrap.min.css", dest: "../dist/sfe" },
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        resolve({ browser: true }),
 | 
				
			||||||
 | 
					        commonjs(),
 | 
				
			||||||
 | 
					        swc({
 | 
				
			||||||
 | 
					            swc: {
 | 
				
			||||||
 | 
					                jsc: {
 | 
				
			||||||
 | 
					                    loose: false,
 | 
				
			||||||
 | 
					                    externalHelpers: false,
 | 
				
			||||||
 | 
					                    // Requires v1.2.50 or upper and requires target to be es2016 or upper.
 | 
				
			||||||
 | 
					                    keepClassNames: false,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                minify: false,
 | 
				
			||||||
 | 
					                env: {
 | 
				
			||||||
 | 
					                    targets: {
 | 
				
			||||||
 | 
					                        edge: "17",
 | 
				
			||||||
 | 
					                        ie: "11",
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    mode: "entry",
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										7
									
								
								web/sfe/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								web/sfe/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "compilerOptions": {
 | 
				
			||||||
 | 
					        "types": ["jquery"],
 | 
				
			||||||
 | 
					        "esModuleInterop": true,
 | 
				
			||||||
 | 
					        "lib": ["DOM", "ES2015", "ES2017"]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -208,7 +208,14 @@ export class AdminOverviewPage extends AdminOverviewBase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return html`<li>
 | 
					            return html`<li>
 | 
				
			||||||
                ${ex(
 | 
					                ${ex(
 | 
				
			||||||
                    () => html`<a href="${url}" class="pf-u-mb-xl" target="_blank">${content}</a>`,
 | 
					                    () =>
 | 
				
			||||||
 | 
					                        html`<a
 | 
				
			||||||
 | 
					                            href="${url}"
 | 
				
			||||||
 | 
					                            class="pf-u-mb-xl"
 | 
				
			||||||
 | 
					                            rel="noopener noreferrer"
 | 
				
			||||||
 | 
					                            target="_blank"
 | 
				
			||||||
 | 
					                            >${content}</a
 | 
				
			||||||
 | 
					                        >`,
 | 
				
			||||||
                    () => html`<a href="${url}" class="pf-u-mb-xl" )>${content}</a>`,
 | 
					                    () => html`<a href="${url}" class="pf-u-mb-xl" )>${content}</a>`,
 | 
				
			||||||
                )}
 | 
					                )}
 | 
				
			||||||
            </li>`;
 | 
					            </li>`;
 | 
				
			||||||
 | 
				
			|||||||
@ -27,12 +27,10 @@ export class RecentEventsCard extends Table<Event> {
 | 
				
			|||||||
    @property({ type: Number })
 | 
					    @property({ type: Number })
 | 
				
			||||||
    pageSize = 10;
 | 
					    pageSize = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Event>> {
 | 
				
			||||||
        return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
 | 
					        return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
 | 
				
			||||||
            ordering: this.order,
 | 
					            ...(await this.defaultEndpointConfig()),
 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: this.pageSize,
 | 
					            pageSize: this.pageSize,
 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -56,6 +56,6 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
 | 
				
			|||||||
            text = this.value.buildHash?.substring(0, 7);
 | 
					            text = this.value.buildHash?.substring(0, 7);
 | 
				
			||||||
            link = `https://github.com/goauthentik/authentik/commit/${this.value.buildHash}`;
 | 
					            link = `https://github.com/goauthentik/authentik/commit/${this.value.buildHash}`;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return html`<a href=${link} target="_blank">${text}</a>`;
 | 
					        return html`<a rel="noopener noreferrer" href=${link} target="_blank">${text}</a>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
import "@goauthentik/admin/applications/ApplicationForm";
 | 
					import "@goauthentik/admin/applications/ApplicationForm";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					import "@goauthentik/components/ak-app-icon";
 | 
				
			||||||
import MDApplication from "@goauthentik/docs/applications/index.md";
 | 
					import MDApplication from "@goauthentik/docs/applications/index.md";
 | 
				
			||||||
import "@goauthentik/elements/Markdown";
 | 
					import "@goauthentik/elements/Markdown";
 | 
				
			||||||
@ -63,12 +62,9 @@ export class ApplicationListPage extends TablePage<Application> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    order = "name";
 | 
					    order = "name";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Application>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Application>> {
 | 
				
			||||||
        return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
 | 
					        return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
 | 
				
			||||||
            ordering: this.order,
 | 
					            ...(await this.defaultEndpointConfig()),
 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
            superuserFullList: true,
 | 
					            superuserFullList: true,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ import "@goauthentik/admin/applications/ApplicationAuthorizeChart";
 | 
				
			|||||||
import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
 | 
					import "@goauthentik/admin/applications/ApplicationCheckAccessForm";
 | 
				
			||||||
import "@goauthentik/admin/applications/ApplicationForm";
 | 
					import "@goauthentik/admin/applications/ApplicationForm";
 | 
				
			||||||
import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
					import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionsPage";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					import "@goauthentik/components/ak-app-icon";
 | 
				
			||||||
@ -11,7 +12,6 @@ import "@goauthentik/elements/EmptyState";
 | 
				
			|||||||
import "@goauthentik/elements/PageHeader";
 | 
					import "@goauthentik/elements/PageHeader";
 | 
				
			||||||
import "@goauthentik/elements/Tabs";
 | 
					import "@goauthentik/elements/Tabs";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionsPage";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { msg } from "@lit/localize";
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
 | 
					import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
 | 
				
			||||||
 | 
				
			|||||||
@ -85,7 +85,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
 | 
				
			|||||||
                    </span>
 | 
					                    </span>
 | 
				
			||||||
                    <button
 | 
					                    <button
 | 
				
			||||||
                        aria-disabled="false"
 | 
					                        aria-disabled="false"
 | 
				
			||||||
                        aria-label="Restore Application Wizard Hint "
 | 
					                        aria-label=${msg("Restore Application Wizard Hint")}
 | 
				
			||||||
                        class="pf-c-button pf-m-plain"
 | 
					                        class="pf-c-button pf-m-plain"
 | 
				
			||||||
                        type="button"
 | 
					                        type="button"
 | 
				
			||||||
                        data-ouia-safe="true"
 | 
					                        data-ouia-safe="true"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,4 @@
 | 
				
			|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
@ -28,12 +27,9 @@ export class ProviderSelectModal extends TableModal<Provider> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    order = "name";
 | 
					    order = "name";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Provider>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
 | 
				
			||||||
        return new ProvidersApi(DEFAULT_CONFIG).providersAllList({
 | 
					        return new ProvidersApi(DEFAULT_CONFIG).providersAllList({
 | 
				
			||||||
            ordering: this.order,
 | 
					            ...(await this.defaultEndpointConfig()),
 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
            backchannel: this.backchannel,
 | 
					            backchannel: this.backchannel,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -67,7 +67,7 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
 | 
				
			|||||||
                .value=${this.wizard.app?.policyEngineMode}
 | 
					                .value=${this.wizard.app?.policyEngineMode}
 | 
				
			||||||
                .errorMessages=${this.wizard.errors.app?.policyEngineMode ?? []}
 | 
					                .errorMessages=${this.wizard.errors.app?.policyEngineMode ?? []}
 | 
				
			||||||
            ></ak-radio-input>
 | 
					            ></ak-radio-input>
 | 
				
			||||||
            <ak-form-group aria-label="UI Settings">
 | 
					            <ak-form-group aria-label=${msg("UI Settings")}>
 | 
				
			||||||
                <span slot="header"> ${msg("UI Settings")} </span>
 | 
					                <span slot="header"> ${msg("UI Settings")} </span>
 | 
				
			||||||
                <div slot="body" class="pf-c-form">
 | 
					                <div slot="body" class="pf-c-form">
 | 
				
			||||||
                    <ak-text-input
 | 
					                    <ak-text-input
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ import {
 | 
				
			|||||||
    SubmitStep,
 | 
					    SubmitStep,
 | 
				
			||||||
} from "@goauthentik/components/ak-wizard-main/commonWizardButtons";
 | 
					} from "@goauthentik/components/ak-wizard-main/commonWizardButtons";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { html } from "lit";
 | 
					import { html } from "lit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "./application/ak-application-wizard-application-details";
 | 
					import "./application/ak-application-wizard-application-details";
 | 
				
			||||||
@ -23,7 +24,7 @@ import { ApplicationStep as ApplicationStepType } from "./types";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ApplicationStep implements ApplicationStepType {
 | 
					class ApplicationStep implements ApplicationStepType {
 | 
				
			||||||
    id = "application";
 | 
					    id = "application";
 | 
				
			||||||
    label = "Application Details";
 | 
					    label = msg("Application Details");
 | 
				
			||||||
    disabled = false;
 | 
					    disabled = false;
 | 
				
			||||||
    valid = false;
 | 
					    valid = false;
 | 
				
			||||||
    get buttons() {
 | 
					    get buttons() {
 | 
				
			||||||
@ -36,7 +37,7 @@ class ApplicationStep implements ApplicationStepType {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ProviderMethodStep implements ApplicationStepType {
 | 
					class ProviderMethodStep implements ApplicationStepType {
 | 
				
			||||||
    id = "provider-method";
 | 
					    id = "provider-method";
 | 
				
			||||||
    label = "Provider Type";
 | 
					    label = msg("Provider Type");
 | 
				
			||||||
    disabled = false;
 | 
					    disabled = false;
 | 
				
			||||||
    valid = false;
 | 
					    valid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -53,7 +54,7 @@ class ProviderMethodStep implements ApplicationStepType {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ProviderStepDetails implements ApplicationStepType {
 | 
					class ProviderStepDetails implements ApplicationStepType {
 | 
				
			||||||
    id = "provider-details";
 | 
					    id = "provider-details";
 | 
				
			||||||
    label = "Provider Configuration";
 | 
					    label = msg("Provider Configuration");
 | 
				
			||||||
    disabled = true;
 | 
					    disabled = true;
 | 
				
			||||||
    valid = false;
 | 
					    valid = false;
 | 
				
			||||||
    get buttons() {
 | 
					    get buttons() {
 | 
				
			||||||
@ -67,7 +68,7 @@ class ProviderStepDetails implements ApplicationStepType {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SubmitApplicationStep implements ApplicationStepType {
 | 
					class SubmitApplicationStep implements ApplicationStepType {
 | 
				
			||||||
    id = "submit";
 | 
					    id = "submit";
 | 
				
			||||||
    label = "Submit Application";
 | 
					    label = msg("Submit Application");
 | 
				
			||||||
    disabled = true;
 | 
					    disabled = true;
 | 
				
			||||||
    valid = false;
 | 
					    valid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -157,6 +157,7 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
 | 
				
			|||||||
                                  ${msg("See more about OCI support here:")} 
 | 
					                                  ${msg("See more about OCI support here:")} 
 | 
				
			||||||
                                  <a
 | 
					                                  <a
 | 
				
			||||||
                                      target="_blank"
 | 
					                                      target="_blank"
 | 
				
			||||||
 | 
					                                      rel="noopener noreferrer"
 | 
				
			||||||
                                      href="${docLink(
 | 
					                                      href="${docLink(
 | 
				
			||||||
                                          "/developer-docs/blueprints/?utm_source=authentik#storage---oci",
 | 
					                                          "/developer-docs/blueprints/?utm_source=authentik#storage---oci",
 | 
				
			||||||
                                      )}"
 | 
					                                      )}"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,13 @@
 | 
				
			|||||||
import "@goauthentik/admin/blueprints/BlueprintForm";
 | 
					import "@goauthentik/admin/blueprints/BlueprintForm";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
					import { EVENT_REFRESH } from "@goauthentik/common/constants";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import { getRelativeTime } from "@goauthentik/common/utils";
 | 
					import { getRelativeTime } from "@goauthentik/common/utils";
 | 
				
			||||||
import "@goauthentik/components/ak-status-label";
 | 
					import "@goauthentik/components/ak-status-label";
 | 
				
			||||||
import "@goauthentik/elements/buttons/ActionButton";
 | 
					import "@goauthentik/elements/buttons/ActionButton";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -68,13 +67,10 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
 | 
				
			|||||||
        return super.styles.concat(PFDescriptionList);
 | 
					        return super.styles.concat(PFDescriptionList);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<BlueprintInstance>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<BlueprintInstance>> {
 | 
				
			||||||
        return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList({
 | 
					        return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList(
 | 
				
			||||||
            ordering: this.order,
 | 
					            await this.defaultEndpointConfig(),
 | 
				
			||||||
            page: page,
 | 
					        );
 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,11 @@
 | 
				
			|||||||
import "@goauthentik/admin/brands/BrandForm";
 | 
					import "@goauthentik/admin/brands/BrandForm";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/components/ak-status-label";
 | 
					import "@goauthentik/components/ak-status-label";
 | 
				
			||||||
import "@goauthentik/components/ak-status-label";
 | 
					import "@goauthentik/components/ak-status-label";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -39,13 +38,8 @@ export class BrandListPage extends TablePage<Brand> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    order = "domain";
 | 
					    order = "domain";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Brand>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Brand>> {
 | 
				
			||||||
        return new CoreApi(DEFAULT_CONFIG).coreBrandsList({
 | 
					        return new CoreApi(DEFAULT_CONFIG).coreBrandsList(await this.defaultEndpointConfig());
 | 
				
			||||||
            ordering: this.order,
 | 
					 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,12 @@
 | 
				
			|||||||
import "@goauthentik/admin/crypto/CertificateGenerateForm";
 | 
					import "@goauthentik/admin/crypto/CertificateGenerateForm";
 | 
				
			||||||
import "@goauthentik/admin/crypto/CertificateKeyPairForm";
 | 
					import "@goauthentik/admin/crypto/CertificateKeyPairForm";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/components/ak-status-label";
 | 
					import "@goauthentik/components/ak-status-label";
 | 
				
			||||||
import { PFColor } from "@goauthentik/elements/Label";
 | 
					import { PFColor } from "@goauthentik/elements/Label";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -53,13 +52,10 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
 | 
				
			|||||||
        return super.styles.concat(PFDescriptionList);
 | 
					        return super.styles.concat(PFDescriptionList);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<CertificateKeyPair>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
 | 
				
			||||||
        return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
 | 
					        return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList(
 | 
				
			||||||
            ordering: this.order,
 | 
					            await this.defaultEndpointConfig(),
 | 
				
			||||||
            page: page,
 | 
					        );
 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import "@goauthentik/admin/enterprise/EnterpriseLicenseForm";
 | 
					import "@goauthentik/admin/enterprise/EnterpriseLicenseForm";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import { getRelativeTime } from "@goauthentik/common/utils";
 | 
					import { getRelativeTime } from "@goauthentik/common/utils";
 | 
				
			||||||
import { PFColor } from "@goauthentik/elements/Label";
 | 
					import { PFColor } from "@goauthentik/elements/Label";
 | 
				
			||||||
import "@goauthentik/elements/Spinner";
 | 
					import "@goauthentik/elements/Spinner";
 | 
				
			||||||
@ -8,7 +8,6 @@ import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			|||||||
import "@goauthentik/elements/cards/AggregateCard";
 | 
					import "@goauthentik/elements/cards/AggregateCard";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -82,18 +81,15 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<License>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<License>> {
 | 
				
			||||||
        this.forecast = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseForecastRetrieve();
 | 
					        this.forecast = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseForecastRetrieve();
 | 
				
			||||||
        this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve();
 | 
					        this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve();
 | 
				
			||||||
        this.installID = (
 | 
					        this.installID = (
 | 
				
			||||||
            await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseGetInstallIdRetrieve()
 | 
					            await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseGetInstallIdRetrieve()
 | 
				
			||||||
        ).installId;
 | 
					        ).installId;
 | 
				
			||||||
        return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseList({
 | 
					        return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseList(
 | 
				
			||||||
            ordering: this.order,
 | 
					            await this.defaultEndpointConfig(),
 | 
				
			||||||
            page: page,
 | 
					        );
 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ import { EventGeo, EventUser } from "@goauthentik/admin/events/utils";
 | 
				
			|||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { EventWithContext } from "@goauthentik/common/events";
 | 
					import { EventWithContext } from "@goauthentik/common/events";
 | 
				
			||||||
import { actionToLabel } from "@goauthentik/common/labels";
 | 
					import { actionToLabel } from "@goauthentik/common/labels";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import { getRelativeTime } from "@goauthentik/common/utils";
 | 
					import { getRelativeTime } from "@goauthentik/common/utils";
 | 
				
			||||||
import "@goauthentik/components/ak-event-info";
 | 
					import "@goauthentik/components/ak-event-info";
 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
@ -45,13 +44,8 @@ export class EventListPage extends TablePage<Event> {
 | 
				
			|||||||
        `);
 | 
					        `);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Event>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Event>> {
 | 
				
			||||||
        return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
 | 
					        return new EventsApi(DEFAULT_CONFIG).eventsEventsList(await this.defaultEndpointConfig());
 | 
				
			||||||
            ordering: this.order,
 | 
					 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,11 @@
 | 
				
			|||||||
import "@goauthentik/admin/events/RuleForm";
 | 
					import "@goauthentik/admin/events/RuleForm";
 | 
				
			||||||
import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
					import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { severityToLabel } from "@goauthentik/common/labels";
 | 
					import { severityToLabel } from "@goauthentik/common/labels";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -47,13 +45,8 @@ export class RuleListPage extends TablePage<NotificationRule> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    order = "name";
 | 
					    order = "name";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<NotificationRule>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<NotificationRule>> {
 | 
				
			||||||
        return new EventsApi(DEFAULT_CONFIG).eventsRulesList({
 | 
					        return new EventsApi(DEFAULT_CONFIG).eventsRulesList(await this.defaultEndpointConfig());
 | 
				
			||||||
            ordering: this.order,
 | 
					 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,10 @@
 | 
				
			|||||||
import "@goauthentik/admin/events/TransportForm";
 | 
					import "@goauthentik/admin/events/TransportForm";
 | 
				
			||||||
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionModal";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/buttons/ActionButton";
 | 
					import "@goauthentik/elements/buttons/ActionButton";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/rbac/ObjectPermissionModal";
 | 
					 | 
				
			||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
					import { TablePage } from "@goauthentik/elements/table/TablePage";
 | 
				
			||||||
@ -43,13 +41,10 @@ export class TransportListPage extends TablePage<NotificationTransport> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    order = "name";
 | 
					    order = "name";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<NotificationTransport>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<NotificationTransport>> {
 | 
				
			||||||
        return new EventsApi(DEFAULT_CONFIG).eventsTransportsList({
 | 
					        return new EventsApi(DEFAULT_CONFIG).eventsTransportsList(
 | 
				
			||||||
            ordering: this.order,
 | 
					            await this.defaultEndpointConfig(),
 | 
				
			||||||
            page: page,
 | 
					        );
 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    columns(): TableColumn[] {
 | 
					    columns(): TableColumn[] {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@ import "@goauthentik/admin/flows/StageBindingForm";
 | 
				
			|||||||
import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
					import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
				
			||||||
import "@goauthentik/admin/stages/StageWizard";
 | 
					import "@goauthentik/admin/stages/StageWizard";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import "@goauthentik/elements/Tabs";
 | 
					import "@goauthentik/elements/Tabs";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
import "@goauthentik/elements/forms/ModalForm";
 | 
					import "@goauthentik/elements/forms/ModalForm";
 | 
				
			||||||
@ -28,12 +27,10 @@ export class BoundStagesList extends Table<FlowStageBinding> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    target?: string;
 | 
					    target?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<FlowStageBinding>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<FlowStageBinding>> {
 | 
				
			||||||
        return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
 | 
					        return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
 | 
				
			||||||
 | 
					            ...(await this.defaultEndpointConfig()),
 | 
				
			||||||
            target: this.target || "",
 | 
					            target: this.target || "",
 | 
				
			||||||
            ordering: this.order,
 | 
					 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,6 @@ import "@goauthentik/admin/flows/FlowForm";
 | 
				
			|||||||
import "@goauthentik/admin/flows/FlowImportForm";
 | 
					import "@goauthentik/admin/flows/FlowImportForm";
 | 
				
			||||||
import { DesignationToLabel } from "@goauthentik/admin/flows/utils";
 | 
					import { DesignationToLabel } from "@goauthentik/admin/flows/utils";
 | 
				
			||||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { uiConfig } from "@goauthentik/common/ui/config";
 | 
					 | 
				
			||||||
import { groupBy } from "@goauthentik/common/utils";
 | 
					import { groupBy } from "@goauthentik/common/utils";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/ConfirmationForm";
 | 
					import "@goauthentik/elements/forms/ConfirmationForm";
 | 
				
			||||||
@ -42,13 +41,8 @@ export class FlowListPage extends TablePage<Flow> {
 | 
				
			|||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    order = "slug";
 | 
					    order = "slug";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async apiEndpoint(page: number): Promise<PaginatedResponse<Flow>> {
 | 
					    async apiEndpoint(): Promise<PaginatedResponse<Flow>> {
 | 
				
			||||||
        return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
 | 
					        return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(await this.defaultEndpointConfig());
 | 
				
			||||||
            ordering: this.order,
 | 
					 | 
				
			||||||
            page: page,
 | 
					 | 
				
			||||||
            pageSize: (await uiConfig()).pagination.perPage,
 | 
					 | 
				
			||||||
            search: this.search || "",
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    groupBy(items: Flow[]): [string, Flow[]][] {
 | 
					    groupBy(items: Flow[]): [string, Flow[]][] {
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user