Compare commits

..

49 Commits

Author SHA1 Message Date
b3b50c5914 core: enforce unique group name on database level
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-11 18:12:39 +01:00
006766cd3c website: bump docusaurus-plugin-openapi-docs from 4.2.0 to 4.3.0 in /website (#12322)
website: bump docusaurus-plugin-openapi-docs in /website

Bumps [docusaurus-plugin-openapi-docs](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/tree/HEAD/packages/docusaurus-plugin-openapi-docs) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/releases)
- [Changelog](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/commits/v4.3.0/packages/docusaurus-plugin-openapi-docs)

---
updated-dependencies:
- dependency-name: docusaurus-plugin-openapi-docs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 15:01:50 +01:00
a79cda924b website: bump docusaurus-theme-openapi-docs from 4.2.0 to 4.3.0 in /website (#12323)
website: bump docusaurus-theme-openapi-docs in /website

Bumps [docusaurus-theme-openapi-docs](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/tree/HEAD/packages/docusaurus-theme-openapi-docs) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/releases)
- [Changelog](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs/commits/v4.3.0/packages/docusaurus-theme-openapi-docs)

---
updated-dependencies:
- dependency-name: docusaurus-theme-openapi-docs
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 14:57:10 +01:00
9d0901effd core: bump goauthentik.io/api/v3 from 3.2024104.2 to 3.2024105.1 (#12324)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024104.2 to 3.2024105.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024104.2...v3.2024105.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 14:57:01 +01:00
89b6a2a2f2 core: bump debugpy from 1.8.9 to 1.8.10 (#12325)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: debugpy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 14:56:46 +01:00
12d9966604 website/integrations: add espoCRM (#11622)
* Create index.md (espoCRM)

Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* Update sidebarsIntegrations.js

added espoCRM integration

* Update index.md

* Update index.md

* Update website/sidebarsIntegrations.js

Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* Update website/integrations/services/espoCRM/index.md

fix integration capitalization

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* Update website/integrations/services/espoCRM/index.md

fix integration capitalization

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* Update index.md

Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>

* applying new suggestions

* fix formatting

* fix formatting again

---------

Signed-off-by: Masked-Kunsiquat <130736043+Masked-Kunsiquat@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 22:21:48 +01:00
accc88fcdd web: bump API Client version (#12320)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-12-10 19:44:21 +01:00
e5dd923333 release: 2024.10.5 (#12319)
* release: 2024.10.5

* manually bump aws version

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 19:20:01 +01:00
29f53fd3a4 website/docs: update and rearchitect the oauth2 provider docs (#11916)
* new topics

* update diagram

* more writing and sidebar entries

* fix file name

* more link fixes

* fix header level

* Optimised images with calibre/image-actions

* rewrite stuff

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* reorganize more

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* messed w/ diagram and added 3 categories

* fixed anchor

* removed whole line

* add link

* more rearrangements

* more content

* edits

* more polishes, rest of Jens' knowledge dump

* more content

* tweaks

* tweak

* argh

* synch with main

* tweaks to test merge

* cleanup

* offline_access

* polish

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 12:03:07 -06:00
cbe5a0d2c8 website/docs: update 2024.10.5 release notes (#12318)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 18:19:58 +01:00
81b3662046 flows: better test stage's challenge responses (#12316)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 17:46:03 +01:00
12ac058547 enterprise/stages/authenticator_endpoint_gdtc: don't set frame options globally (#12311)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 15:17:00 +01:00
69cd0ef565 stages/identification: fix invalid challenge warning when no captcha stage is set (#12312)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 15:16:44 +01:00
83edb0dcb8 website/docs: prepare 2024.10.5 release notes (#12309)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 14:15:24 +01:00
0c80b1b8c3 website: bump nanoid from 3.3.7 to 3.3.8 in /website (#12307)
Bumps [nanoid](https://github.com/ai/nanoid) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.7...3.3.8)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 13:43:40 +01:00
47e330d08a flows: silent authz flow (#12213)
* flows: add FlowPlan .to_redirect helper to redirect to flow executor

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* flows: add initial silent flow executor

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* more cleanup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* refactor and add tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* how'd that happen

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix most tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't set allowed_silent_types if we cant transmit data via URL

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix stage not being set early enough

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix OAuthDeviceCodeFinishStage being marked able-to-be-skipped-to when it is not

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix more tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont skip on rac for now

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add support for SAML redirect

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-10 13:43:22 +01:00
97676d28a7 root: use healthcheck in depends_on for postgres and redis (#12301)
Docker Compose Updates Healthcheck

The Healthcheck is not used for the depends_on option. This change ensures all dependencies are all ready to work before starting worker and server container.

Signed-off-by: Kaindl Network <82705244+kaindlnetwork@users.noreply.github.com>
2024-12-10 13:10:57 +01:00
b9435870c0 ci: ensure mark jobs always run and reflect correct status (#12288)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-12-10 13:09:41 +01:00
81ae02e623 enterprise: allow deletion/modification of users when in read-only mode (#12289)
* enterprise: allow deletion/modification of users when in read-only mode

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* actually 10.5+

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Jens L. <jens@beryju.org>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-12-10 13:07:50 +01:00
28a23110c2 web/flows: resize captcha iframes (#12260)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* web: streamline CaptchaStage

# What

This commit:

1. Replaces the mass of `if () { if() { if() } }` with two state tables:
  - One for `render()`
  - One for `renderBody()`

2. Breaks each Captcha out into "interactive" and "executive" versions
3. Creates a handler table for each Captcha type
4. Replaces the `.forEach` expression with a `for` loop.
5. Move `updated` to the end of the class.
6. Make captchDocument and captchaFrame constructed-on-demand with a cache.
7. Remove a lot of `@state` handlers
8. Give IframeMessageEvent its own type.
9. Removed `this.scriptElement`
10. Replaced `window.removeEventListener` with an `AbortController()`
# Why

1. **Replacing `if` trees with a state table.** The logic of the original was really hard to follow.
   With the state table, we can clearly see now that for the `render()` function, we care about the
   Boolean flags `[embedded, challenged, interactive]` and have appropriate effects for each. With
   `renderBody()`, we can see that we care about the Boolean flags `[hasError, challenged]`, and can
   see the appropriate effects for each one.

2. (and 3.) **Breaking each Captcha clause into separate handlers.** Again, the logic was convoluted,
   when what we really care about is "Does this captcha have a corresponding handler attached to
   `window`, and, if so, should we run the interactive or executive version of it?" By putting all
   of that into a table of `[name, challenge, execute]`, we can clearly see what's being handled
   when.

4. **Replacing `foreach()` with `for()`**: [You cannot use a `forEach()` with async
   expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#:~:text=does%20not%20wait%20for%20promises).
   If you need asynchronous behavior in an ordered loop, `for()` is the safest way to handle it; if
   you need asynchronous behavior from multiple promises, `Promise.allSettled(handlers.map())` is
   the way to go.

   I tried to tell if this function *meant* to run every handler it found simultaneously, or if it
   meant to test them in order; I went with the second option, breaking and exiting the loop once a
   handler had run successfully.

5. **Reordered the code a bit**. We're trying to evolve a pattern in our source code: styles,
   properties, states, internal variables, constructor, getters & setters that are not `@property()`
   or `@state()`, DOM-related lifecycle handlers, event handlers, pre-render lifecycle handlers,
   renderers, and post-render lifecycle handlers. Helper methods (including subrenderers) go above
   the method(s) they help.

6. **Constructing Elements on demand with a cache**. It is not guaranteed that we will actually need
   either of those. Constructing them on demand with a cache is both performant and cleaner.
   Likewise, I removed these from the Lit object's `state()` table, since they're constructed once
   and never change over the lifetime of an instance of `ak-stage-captcha`.

9. **Remove `this.scriptElement`**: It was never referenced outside the one function where it was used.

10. **Remove `removeEventListener()`**: `AbortController()` is a bit more verbose for small event
    handler collections, but it's considered much more reliable and much cleaner.

* Didn't save the extracted ListenerController.
2024-12-09 09:11:04 -08:00
a117918cd6 website/docs: add page about the Cobalt pentest (#12249)
* draft for collab

* links

* added link to see all audits

* corrections and fix explanations

Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>

---------

Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Fletcher Heisler <fheisler@users.noreply.github.com>
2024-12-09 07:57:34 -06:00
f1a548f941 core: bump aws-cdk-lib from 2.171.1 to 2.172.0 (#12296)
Bumps [aws-cdk-lib](https://github.com/aws/aws-cdk) from 2.171.1 to 2.172.0.
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/v2.172.0/CHANGELOG.v2.md)
- [Commits](https://github.com/aws/aws-cdk/compare/v2.171.1...v2.172.0)

---
updated-dependencies:
- dependency-name: aws-cdk-lib
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 14:39:51 +01:00
021c0f7cb9 website: bump aws-cdk from 2.171.1 to 2.172.0 in /website (#12295)
Bumps [aws-cdk](https://github.com/aws/aws-cdk/tree/HEAD/packages/aws-cdk) from 2.171.1 to 2.172.0.
- [Release notes](https://github.com/aws/aws-cdk/releases)
- [Changelog](https://github.com/aws/aws-cdk/blob/v2.172.0/CHANGELOG.v2.md)
- [Commits](https://github.com/aws/aws-cdk/commits/v2.172.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 14:39:40 +01:00
c09ce06812 core: bump sentry-sdk from 2.19.1 to 2.19.2 (#12297)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.19.1 to 2.19.2.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.19.1...2.19.2)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 14:39:14 +01:00
e0aa588e60 core: bump coverage from 7.6.8 to 7.6.9 (#12299)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.6.8 to 7.6.9.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.6.8...7.6.9)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 14:35:30 +01:00
e842a73f3b core, web: update translations (#12290)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-12-07 21:13:41 +02:00
98c43447ff root: fix override locale only if it is not empty (#12283)
* update impersonate middleware

override language only if it's not empty

Signed-off-by: Zhang Jie <13131481+j-z10@users.noreply.github.com>

* Update brands locale scope

Signed-off-by: Zhang Jie <13131481+j-z10@users.noreply.github.com>

---------

Signed-off-by: Zhang Jie <13131481+j-z10@users.noreply.github.com>
2024-12-06 23:58:02 +02:00
eef1237297 translate: Updates for file web/xliff/en.xlf in fr (#12276)
Translate web/xliff/en.xlf in fr

100% translated source file: 'web/xliff/en.xlf'
on 'fr'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-12-06 22:13:08 +02:00
dea0a34dd9 core: bump twilio from 9.3.7 to 9.3.8 (#12282)
Bumps [twilio](https://github.com/twilio/twilio-python) from 9.3.7 to 9.3.8.
- [Release notes](https://github.com/twilio/twilio-python/releases)
- [Changelog](https://github.com/twilio/twilio-python/blob/main/CHANGES.md)
- [Commits](https://github.com/twilio/twilio-python/compare/9.3.7...9.3.8)

---
updated-dependencies:
- dependency-name: twilio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-06 22:12:04 +02:00
f3359fb4d0 website: bump path-to-regexp and express in /website (#12279)
Bumps [path-to-regexp](https://github.com/pillarjs/path-to-regexp) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `path-to-regexp` from 0.1.10 to 1.9.0
- [Release notes](https://github.com/pillarjs/path-to-regexp/releases)
- [Changelog](https://github.com/pillarjs/path-to-regexp/blob/master/History.md)
- [Commits](https://github.com/pillarjs/path-to-regexp/compare/v0.1.10...v1.9.0)

Updates `express` from 4.21.1 to 4.21.2
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.2/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.1...4.21.2)

---
updated-dependencies:
- dependency-name: path-to-regexp
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-06 22:11:51 +02:00
d344db9c2a core: bump sentry-sdk from 2.19.0 to 2.19.1 (#12280)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.19.0 to 2.19.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.19.0...2.19.1)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-06 22:11:41 +02:00
13b1811268 core: bump ruff from 0.8.1 to 0.8.2 (#12281)
* core: bump ruff from 0.8.1 to 0.8.2

Bumps [ruff](https://github.com/astral-sh/ruff) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.8.1...0.8.2)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format v2

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-12-06 22:11:31 +02:00
84bc0b6fdd website/docs: fix lint (#12287) 2024-12-06 20:09:16 +00:00
5c2853bf73 website/integrations: netbird: fix redirect URI regex (#12284) 2024-12-06 15:03:52 +00:00
242546e621 web: simplify ?inline handler for Storybook (#12246)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* web: simplify `?inline` handler for Storybook

# What

- Revise the `?inline` handler for Storybook
- Enable headless test runs of E2E
- Reduce headless testing to single instances

# Why

## `?inline` handling

Vite-for-Storybook-for-Web-Components has a requirement that all component CSS imports be
suffixed with an `?inline` argument so Vite knows to put the CSS into the component and
not inject it into the document head.

This `?inline` argument is an implementation detail of Storybook. It would be irrelevant clutter
added to our codebase. We were using `rollup-plugin-modify` to find every instance of an
import-to-component, but the implementation was clunky and involved scanning the source code
manually.

`rollup-plugin-modify` version 3 has regular expressions and takes a function as an argument. This
allows us to generate the CSS import maps on-the-fly when Storybook is run, eliminating a fragile
build step.  We can also remove the source code scanner for those imports.

## Changes to testing

It's just nice to be able to run the E2E tests headlessly, without them eating up your screen real
estate, flashing, or grabbing your mouse.

WebdriverIO's testing of Web Components is new and, as we've seen, a bit cranky. The WebdriverIO
team currently recommends not running the tests in parallel. We only have about 70 tests so far, and
they're fairly speedy, especially when you don't have to invoke a browser session for every test.
2024-12-05 10:16:10 -08:00
b75672ff0e website/docs: Update Traefik middleware example to reflect latest version of Traefik (#12267)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-12-05 18:09:13 +00:00
672ba72c8a website/docs: add . in https://netbird.company* (#12166)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-12-05 18:06:06 +00:00
d618f48bff core: bump goauthentik.io/api/v3 from 3.2024104.1 to 3.2024104.2 (#12263)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024104.1 to 3.2024104.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Changelog](https://github.com/goauthentik/client-go/blob/main/model_version_history.go)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024104.1...v3.2024104.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 15:02:03 +02:00
716f2dff74 core: bump pydantic from 2.10.2 to 2.10.3 (#12262)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.10.2 to 2.10.3.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.10.2...v2.10.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 15:01:11 +02:00
fc8a0b5ce0 core: bump github.com/getsentry/sentry-go from 0.29.1 to 0.30.0 (#12264)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.29.1 to 0.30.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.29.1...v0.30.0)

---
updated-dependencies:
- dependency-name: github.com/getsentry/sentry-go
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:58:53 +02:00
2b079816bb core, web: update translations (#12268)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-12-05 14:58:35 +02:00
eded745332 website: bump @types/react from 18.3.12 to 18.3.13 in /website (#12269)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.12 to 18.3.13.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:58:04 +02:00
1740ab938d website: bump prettier from 3.4.1 to 3.4.2 in /website (#12270)
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.1 to 3.4.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.1...3.4.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:57:52 +02:00
480f305f29 ci: bump actions/attest-build-provenance from 1 to 2 (#12271)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 1 to 2.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](https://github.com/actions/attest-build-provenance/compare/v1...v2)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:57:28 +02:00
90cc36fdee core: bump golang.org/x/sync from 0.9.0 to 0.10.0 (#12272)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/golang/sync/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:54:52 +02:00
cf5b951a8c core: bump django from 5.0.9 to 5.0.10 (#12273)
Bumps [django](https://github.com/django/django) from 5.0.9 to 5.0.10.
- [Commits](https://github.com/django/django/compare/5.0.9...5.0.10)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:54:39 +02:00
9a9d8e4cf7 core: bump webauthn from 2.3.0 to 2.4.0 (#12274)
Bumps [webauthn](https://github.com/duo-labs/py_webauthn) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/duo-labs/py_webauthn/releases)
- [Changelog](https://github.com/duo-labs/py_webauthn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/duo-labs/py_webauthn/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: webauthn
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-05 14:53:20 +02:00
695de453ec website/integrations: add The Lounge (#11971)
* Add The Lounge Integration guide

Signed-off-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>

* Add The Lounge to sidebar, fix Note section formatting

* Fix folder name (1/2)

* Fix folder name (2/2)

* Update website/integrations/services/thelounge/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>

* Update website/integrations/services/thelounge/index.md

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>

* Fix cutoff sentence

Signed-off-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>

* Linter fixes

---------

Signed-off-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-12-04 13:21:26 -06:00
d3a581b614 core: bump python-kadmin-rs from 0.3.0 to 0.4.0 (#12257) 2024-12-04 10:38:36 +02:00
89 changed files with 1684 additions and 996 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2024.10.4
current_version = 2024.10.5
tag = 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*))?

View File

@ -36,8 +36,11 @@ jobs:
poetry run make aws-cfn
git diff --exit-code
ci-aws-cfn-mark:
if: always()
needs:
- check-changes-applied
runs-on: ubuntu-latest
steps:
- run: echo mark
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

View File

@ -209,6 +209,7 @@ jobs:
file: unittest.xml
token: ${{ secrets.CODECOV_TOKEN }}
ci-core-mark:
if: always()
needs:
- lint
- test-migrations
@ -218,7 +219,9 @@ jobs:
- test-e2e
runs-on: ubuntu-latest
steps:
- run: echo mark
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
build:
strategy:
fail-fast: false
@ -275,7 +278,7 @@ jobs:
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-server:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-server:buildcache,mode=max' || '' }}
platforms: linux/${{ matrix.arch }}
- uses: actions/attest-build-provenance@v1
- uses: actions/attest-build-provenance@v2
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@ -49,12 +49,15 @@ jobs:
run: |
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./...
ci-outpost-mark:
if: always()
needs:
- lint-golint
- test-unittest
runs-on: ubuntu-latest
steps:
- run: echo mark
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
build-container:
timeout-minutes: 120
needs:
@ -111,7 +114,7 @@ jobs:
context: .
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && format('type=registry,ref=ghcr.io/goauthentik/dev-{0}:buildcache,mode=max', matrix.type) || '' }}
- uses: actions/attest-build-provenance@v1
- uses: actions/attest-build-provenance@v2
id: attest
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
with:

View File

@ -61,12 +61,15 @@ jobs:
working-directory: web/
run: npm run build
ci-web-mark:
if: always()
needs:
- build
- lint
runs-on: ubuntu-latest
steps:
- run: echo mark
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
test:
needs:
- ci-web-mark

View File

@ -62,10 +62,13 @@ jobs:
working-directory: website/
run: npm run ${{ matrix.job }}
ci-website-mark:
if: always()
needs:
- lint
- test
- build
runs-on: ubuntu-latest
steps:
- run: echo mark
- uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

View File

@ -55,7 +55,7 @@ jobs:
VERSION=${{ github.ref }}
tags: ${{ steps.ev.outputs.imageTags }}
platforms: linux/amd64,linux/arm64
- uses: actions/attest-build-provenance@v1
- uses: actions/attest-build-provenance@v2
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}
@ -119,7 +119,7 @@ jobs:
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
context: .
- uses: actions/attest-build-provenance@v1
- uses: actions/attest-build-provenance@v2
id: attest
with:
subject-name: ${{ steps.ev.outputs.attestImageNames }}

View File

@ -2,7 +2,7 @@ authentik takes security very seriously. We follow the rules of [responsible di
## Independent audits and pentests
In May/June of 2023 [Cure53](https://cure53.de) conducted an audit and pentest. The [results](https://cure53.de/pentest-report_authentik.pdf) are published on the [Cure53 website](https://cure53.de/#publications-2023). For more details about authentik's response to the findings of the audit refer to [2023-06 Cure53 Code audit](https://goauthentik.io/docs/security/2023-06-cure53).
We are committed to engaging in regular pentesting and security audits of authentik. Defining and adhering to a cadence of external testing ensures a stronger probability that our code base, our features, and our architecture is as secure and non-exploitable as possible. For more details about specfic audits and pentests, refer to "Audits and Certificates" in our [Security documentation]](https://docs.goauthentik.io/docs/security).
## What authentik classifies as a CVE

View File

@ -2,7 +2,7 @@
from os import environ
__version__ = "2024.10.4"
__version__ = "2024.10.5"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -25,5 +25,7 @@ class BrandMiddleware:
locale = brand.default_locale
if locale != "":
locale_to_set = locale
with override(locale_to_set):
return self.get_response(request)
if locale_to_set:
with override(locale_to_set):
return self.get_response(request)
return self.get_response(request)

View File

@ -103,9 +103,6 @@ class GroupSerializer(ModelSerializer):
"users": {
"default": list,
},
# TODO: This field isn't unique on the database which is hard to backport
# hence we just validate the uniqueness here
"name": {"validators": [UniqueValidator(Group.objects.all())]},
}

View File

@ -42,8 +42,10 @@ class ImpersonateMiddleware:
# Ensure that the user is active, otherwise nothing will work
request.user.is_active = True
with override(locale_to_set):
return self.get_response(request)
if locale_to_set:
with override(locale_to_set):
return self.get_response(request)
return self.get_response(request)
class RequestIDMiddleware:

View File

@ -0,0 +1,21 @@
# Generated by Django 5.0.8 on 2024-08-08 12:09
from django.db import migrations, models
from authentik.lib.migrations import fallback_names
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0039_source_group_matching_mode_alter_group_name_and_more"),
]
operations = [
migrations.RunPython(fallback_names("authentik_core", "group", "name")),
migrations.AlterField(
model_name="group",
name="name",
field=models.TextField(unique=True, verbose_name="name"),
),
]

View File

@ -173,7 +173,7 @@ class Group(SerializerModel, AttributesMixin):
group_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.TextField(_("name"))
name = models.TextField(verbose_name=_("name"), unique=True)
is_superuser = models.BooleanField(
default=False, help_text=_("Users added to this group will be superusers.")
)

View File

@ -265,12 +265,7 @@ class SourceFlowManager:
if stages:
for stage in stages:
plan.append_stage(stage)
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=flow.slug,
)
return plan.to_redirect(self.request, flow)
def handle_auth(
self,

View File

@ -17,10 +17,8 @@ from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner
from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import (
SESSION_KEY_APPLICATION_PRE,
SESSION_KEY_PLAN,
ToDefaultFlow,
)
from authentik.lib.utils.urls import redirect_with_qs
from authentik.stages.consent.stage import (
PLAN_CONTEXT_CONSENT_HEADER,
PLAN_CONTEXT_CONSENT_PERMISSIONS,
@ -58,8 +56,7 @@ class RedirectToAppLaunch(View):
except FlowNonApplicableException:
raise Http404 from None
plan.insert_stage(in_memory_stage(RedirectToAppStage))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs("authentik_core:if-flow", request.GET, flow_slug=flow.slug)
return plan.to_redirect(request, flow)
class RedirectToAppStage(ChallengeStageView):

View File

@ -6,6 +6,7 @@ from django.http import HttpRequest, HttpResponse, JsonResponse
from django.urls import resolve
from structlog.stdlib import BoundLogger, get_logger
from authentik.core.api.users import UserViewSet
from authentik.enterprise.api import LicenseViewSet
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import LicenseUsageStatus
@ -59,6 +60,9 @@ class EnterpriseMiddleware:
# Flow executor is mounted as an API path but explicitly allowed
if request.resolver_match._func_path == class_to_path(FlowExecutorView):
return True
# Always allow making changes to users, even in case the license has ben exceeded
if request.resolver_match._func_path == class_to_path(UserViewSet):
return True
# Only apply these restrictions to the API
if "authentik_api" not in request.resolver_match.app_names:
return True

View File

@ -18,9 +18,7 @@ from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner
from authentik.flows.stage import RedirectStage
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.engine import PolicyEngine
@ -56,12 +54,7 @@ class RACStartView(EnterprisePolicyAccessView):
provider=self.provider,
)
)
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
request.GET,
flow_slug=self.provider.authorization_flow.slug,
)
return plan.to_redirect(request, self.provider.authorization_flow)
class RACInterface(InterfaceView):

View File

@ -4,7 +4,9 @@ from typing import Any
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.clickjacking import xframe_options_sameorigin
from googleapiclient.discovery import build
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
@ -26,6 +28,7 @@ HEADER_ACCESS_CHALLENGE_RESPONSE = "X-Verified-Access-Challenge-Response"
DEVICE_TRUST_VERIFIED_ACCESS = "VerifiedAccess"
@method_decorator(xframe_options_sameorigin, name="dispatch")
class GoogleChromeDeviceTrustConnector(View):
"""Google Chrome Device-trust connector based endpoint authenticator"""

View File

@ -215,3 +215,49 @@ class TestReadOnly(FlowTestCase):
{"detail": "Request denied due to expired/invalid license.", "code": "denied_license"},
)
self.assertEqual(response.status_code, 400)
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
@patch(
"authentik.enterprise.license.LicenseKey.get_internal_user_count",
MagicMock(return_value=1000),
)
@patch(
"authentik.enterprise.license.LicenseKey.get_external_user_count",
MagicMock(return_value=1000),
)
@patch(
"authentik.enterprise.license.LicenseKey.record_usage",
MagicMock(),
)
def test_manage_users(self):
"""Test that managing users is still possible"""
License.objects.create(key=generate_id())
usage = LicenseUsage.objects.create(
internal_user_count=100,
external_user_count=100,
status=LicenseUsageStatus.VALID,
)
usage.record_date = now() - timedelta(weeks=THRESHOLD_READ_ONLY_WEEKS + 1)
usage.save(update_fields=["record_date"])
admin = create_test_admin_user()
self.client.force_login(admin)
# Reading is always allowed
response = self.client.get(reverse("authentik_api:user-list"))
self.assertEqual(response.status_code, 200)
# Writing should also be allowed
response = self.client.patch(reverse("authentik_api:user-detail", kwargs={"pk": admin.pk}))
self.assertEqual(response.status_code, 200)

View File

@ -1,10 +1,10 @@
"""Flows Planner"""
from dataclasses import dataclass, field
from typing import Any
from typing import TYPE_CHECKING, Any
from django.core.cache import cache
from django.http import HttpRequest
from django.http import HttpRequest, HttpResponse
from sentry_sdk import start_span
from sentry_sdk.tracing import Span
from structlog.stdlib import BoundLogger, get_logger
@ -23,10 +23,15 @@ from authentik.flows.models import (
in_memory_stage,
)
from authentik.lib.config import CONFIG
from authentik.lib.utils.urls import redirect_with_qs
from authentik.outposts.models import Outpost
from authentik.policies.engine import PolicyEngine
from authentik.root.middleware import ClientIPMiddleware
if TYPE_CHECKING:
from authentik.flows.stage import StageView
LOGGER = get_logger()
PLAN_CONTEXT_PENDING_USER = "pending_user"
PLAN_CONTEXT_SSO = "is_sso"
@ -110,6 +115,54 @@ class FlowPlan:
"""Check if there are any stages left in this plan"""
return len(self.markers) + len(self.bindings) > 0
def requires_flow_executor(
self,
allowed_silent_types: list["StageView"] | None = None,
):
# Check if we actually need to show the Flow executor, or if we can jump straight to the end
found_unskippable = True
if allowed_silent_types:
LOGGER.debug("Checking if we can skip the flow executor...")
# Policies applied to the flow have already been evaluated, so we're checking for stages
# allow-listed or bindings that require a policy re-eval
found_unskippable = False
for binding, marker in zip(self.bindings, self.markers, strict=True):
if binding.stage.view not in allowed_silent_types:
found_unskippable = True
if marker and isinstance(marker, ReevaluateMarker):
found_unskippable = True
LOGGER.debug("Required flow executor status", status=found_unskippable)
return found_unskippable
def to_redirect(
self,
request: HttpRequest,
flow: Flow,
allowed_silent_types: list["StageView"] | None = None,
) -> HttpResponse:
"""Redirect to the flow executor for this flow plan"""
from authentik.flows.views.executor import (
SESSION_KEY_PLAN,
FlowExecutorView,
)
request.session[SESSION_KEY_PLAN] = self
requires_flow_executor = self.requires_flow_executor(allowed_silent_types)
if not requires_flow_executor:
# No unskippable stages found, so we can directly return the response of the last stage
final_stage: type[StageView] = self.bindings[-1].stage.view
temp_exec = FlowExecutorView(flow=flow, request=request, plan=self)
temp_exec.current_stage = self.bindings[-1].stage
stage = final_stage(request=request, executor=temp_exec)
return stage.dispatch(request)
return redirect_with_qs(
"authentik_core:if-flow",
request.GET,
flow_slug=flow.slug,
)
class FlowPlanner:
"""Execute all policies to plan out a flat list of all Stages

View File

@ -2,6 +2,7 @@
from typing import TYPE_CHECKING
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import HttpRequest
from django.http.request import QueryDict
@ -224,6 +225,14 @@ class ChallengeStageView(StageView):
full_errors[field].append(field_error)
challenge_response.initial_data["response_errors"] = full_errors
if not challenge_response.is_valid():
if settings.TEST:
raise StageInvalidException(
(
f"Invalid challenge response: \n\t{challenge_response.errors}"
f"\n\nValidated data:\n\t {challenge_response.data}"
f"\n\nInitial data:\n\t {challenge_response.initial_data}"
),
)
self.logger.error(
"f(ch): invalid challenge response",
errors=challenge_response.errors,

View File

@ -5,6 +5,8 @@ from unittest.mock import MagicMock, Mock, PropertyMock, patch
from django.contrib.auth.models import AnonymousUser
from django.contrib.sessions.middleware import SessionMiddleware
from django.core.cache import cache
from django.http import HttpRequest
from django.shortcuts import redirect
from django.test import RequestFactory, TestCase
from django.urls import reverse
from guardian.shortcuts import get_anonymous_user
@ -14,8 +16,14 @@ from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from authentik.flows.markers import ReevaluateMarker, StageMarker
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation, FlowStageBinding
from authentik.flows.models import (
FlowAuthenticationRequirement,
FlowDesignation,
FlowStageBinding,
in_memory_stage,
)
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
from authentik.flows.stage import StageView
from authentik.lib.tests.utils import dummy_get_response
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.models import Outpost
@ -211,3 +219,99 @@ class TestFlowPlanner(TestCase):
self.assertIsInstance(plan.markers[0], StageMarker)
self.assertIsInstance(plan.markers[1], ReevaluateMarker)
def test_to_redirect(self):
"""Test to_redirect and skipping the flow executor"""
flow = create_test_flow()
flow.authentication = FlowAuthenticationRequirement.NONE
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(request)
request.session.save()
request.user = AnonymousUser()
planner = FlowPlanner(flow)
planner.allow_empty_flows = True
plan = planner.plan(request)
self.assertTrue(plan.requires_flow_executor())
self.assertEqual(
plan.to_redirect(request, flow).url,
reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug}),
)
def test_to_redirect_skip_simple(self):
"""Test to_redirect and skipping the flow executor"""
flow = create_test_flow()
flow.authentication = FlowAuthenticationRequirement.NONE
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
middleware = SessionMiddleware(dummy_get_response)
middleware.process_request(request)
request.session.save()
request.user = AnonymousUser()
planner = FlowPlanner(flow)
planner.allow_empty_flows = True
plan = planner.plan(request)
class TStageView(StageView):
def dispatch(self, request: HttpRequest, *args, **kwargs):
return redirect("https://authentik.company")
plan.append_stage(in_memory_stage(TStageView))
self.assertFalse(plan.requires_flow_executor(allowed_silent_types=[TStageView]))
self.assertEqual(
plan.to_redirect(request, flow, allowed_silent_types=[TStageView]).url,
"https://authentik.company",
)
def test_to_redirect_skip_stage(self):
"""Test to_redirect and skipping the flow executor
(with a stage bound that cannot be skipped)"""
flow = create_test_flow()
flow.authentication = FlowAuthenticationRequirement.NONE
FlowStageBinding.objects.create(
target=flow, stage=DummyStage.objects.create(name="dummy"), order=0
)
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
request.user = AnonymousUser()
planner = FlowPlanner(flow)
planner.allow_empty_flows = True
plan = planner.plan(request)
class TStageView(StageView):
def dispatch(self, request: HttpRequest, *args, **kwargs):
return redirect("https://authentik.company")
plan.append_stage(in_memory_stage(TStageView))
self.assertTrue(plan.requires_flow_executor(allowed_silent_types=[TStageView]))
def test_to_redirect_skip_policies(self):
"""Test to_redirect and skipping the flow executor
(with a marker on the stage view type that can be skipped)
Note that this is not actually used anywhere in the code, all stages that are dynamically
added are statically added"""
flow = create_test_flow()
flow.authentication = FlowAuthenticationRequirement.NONE
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
request.user = AnonymousUser()
planner = FlowPlanner(flow)
planner.allow_empty_flows = True
plan = planner.plan(request)
class TStageView(StageView):
def dispatch(self, request: HttpRequest, *args, **kwargs):
return redirect("https://authentik.company")
plan.append_stage(in_memory_stage(TStageView), ReevaluateMarker(None))
self.assertTrue(plan.requires_flow_executor(allowed_silent_types=[TStageView]))

View File

@ -597,9 +597,4 @@ class ConfigureFlowInitView(LoginRequiredMixin, View):
except FlowNonApplicableException:
LOGGER.warning("Flow not applicable to user")
raise Http404 from None
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=stage.configure_flow.slug,
)
return plan.to_redirect(request, stage.configure_flow)

View File

@ -311,7 +311,7 @@ class TestAuthorize(OAuthTestCase):
user = create_test_admin_user()
self.client.force_login(user)
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -320,16 +320,10 @@ class TestAuthorize(OAuthTestCase):
"redirect_uri": "foo://localhost",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": f"foo://localhost?code={code.code}&state={state}",
},
self.assertEqual(
response.url,
f"foo://localhost?code={code.code}&state={state}",
)
self.assertAlmostEqual(
code.expires.timestamp() - now().timestamp(),
@ -377,7 +371,7 @@ class TestAuthorize(OAuthTestCase):
),
):
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "id_token",
@ -388,22 +382,16 @@ class TestAuthorize(OAuthTestCase):
"nonce": generate_id(),
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
token: AccessToken = AccessToken.objects.filter(user=user).first()
expires = timedelta_from_string(provider.access_token_validity).total_seconds()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": (
f"http://localhost#access_token={token.token}"
f"&id_token={provider.encode(token.id_token.to_dict())}"
f"&token_type={TOKEN_TYPE}"
f"&expires_in={int(expires)}&state={state}"
),
},
self.assertEqual(
response.url,
(
f"http://localhost#access_token={token.token}"
f"&id_token={provider.encode(token.id_token.to_dict())}"
f"&token_type={TOKEN_TYPE}"
f"&expires_in={int(expires)}&state={state}"
),
)
jwt = self.validate_jwt(token, provider)
self.assertEqual(jwt["amr"], ["pwd"])
@ -455,7 +443,7 @@ class TestAuthorize(OAuthTestCase):
),
):
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "id_token",
@ -466,10 +454,7 @@ class TestAuthorize(OAuthTestCase):
"nonce": generate_id(),
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.status_code, 302)
token: AccessToken = AccessToken.objects.filter(user=user).first()
expires = timedelta_from_string(provider.access_token_validity).total_seconds()
jwt = self.validate_jwe(token, provider)
@ -506,7 +491,7 @@ class TestAuthorize(OAuthTestCase):
),
):
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -518,16 +503,10 @@ class TestAuthorize(OAuthTestCase):
"nonce": generate_id(),
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": (f"http://localhost#code={code.code}" f"&state={state}"),
},
self.assertEqual(
response.url,
f"http://localhost#code={code.code}&state={state}",
)
self.assertAlmostEqual(
code.expires.timestamp() - now().timestamp(),

View File

@ -45,7 +45,7 @@ class TestTokenPKCE(OAuthTestCase):
challenge = generate_id()
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -56,16 +56,10 @@ class TestTokenPKCE(OAuthTestCase):
"code_challenge_method": "S256",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": f"foo://localhost?code={code.code}&state={state}",
},
self.assertEqual(
response.url,
f"foo://localhost?code={code.code}&state={state}",
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
@ -107,7 +101,7 @@ class TestTokenPKCE(OAuthTestCase):
self.client.force_login(user)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -118,16 +112,10 @@ class TestTokenPKCE(OAuthTestCase):
# "code_challenge_method": "S256",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": f"foo://localhost?code={code.code}&state={state}",
},
self.assertEqual(
response.url,
f"foo://localhost?code={code.code}&state={state}",
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
@ -174,7 +162,7 @@ class TestTokenPKCE(OAuthTestCase):
)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -185,16 +173,10 @@ class TestTokenPKCE(OAuthTestCase):
"code_challenge_method": "S256",
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": f"foo://localhost?code={code.code}&state={state}",
},
self.assertEqual(
response.url,
f"foo://localhost?code={code.code}&state={state}",
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
@ -225,7 +207,7 @@ class TestTokenPKCE(OAuthTestCase):
verifier = generate_id()
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
# Step 1, initiate params and get redirect to flow
self.client.get(
response = self.client.get(
reverse("authentik_providers_oauth2:authorize"),
data={
"response_type": "code",
@ -235,16 +217,10 @@ class TestTokenPKCE(OAuthTestCase):
"code_challenge": verifier,
},
)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
code: AuthorizationCode = AuthorizationCode.objects.filter(user=user).first()
self.assertJSONEqual(
response.content.decode(),
{
"component": "xak-flow-redirect",
"to": f"foo://localhost?code={code.code}&state={state}",
},
self.assertEqual(
response.url,
f"foo://localhost?code={code.code}&state={state}",
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),

View File

@ -27,9 +27,7 @@ from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO, FlowPlanner
from authentik.flows.stage import StageView
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import bad_request_message
from authentik.policies.types import PolicyRequest
from authentik.policies.views import PolicyAccessView, RequestValidationError
@ -454,11 +452,16 @@ class AuthorizationFlowInitView(PolicyAccessView):
plan.append_stage(in_memory_stage(OAuthFulfillmentStage))
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=self.provider.authorization_flow.slug,
return plan.to_redirect(
self.request,
self.provider.authorization_flow,
# We can only skip the flow executor and directly go to the final redirect URL if
# we can submit the data to the RP via URL
allowed_silent_types=(
[OAuthFulfillmentStage]
if self.params.response_mode in [ResponseMode.QUERY, ResponseMode.FRAGMENT]
else []
),
)

View File

@ -16,7 +16,6 @@ from authentik.flows.models import in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO, FlowPlanner
from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.views import PolicyAccessView
from authentik.providers.oauth2.models import DeviceToken
from authentik.providers.oauth2.views.device_finish import (
@ -73,12 +72,7 @@ class CodeValidatorView(PolicyAccessView):
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=self.token.provider.authorization_flow.slug,
)
return plan.to_redirect(self.request, self.token.provider.authorization_flow)
class DeviceEntryView(PolicyAccessView):
@ -109,11 +103,7 @@ class DeviceEntryView(PolicyAccessView):
plan.append_stage(in_memory_stage(OAuthDeviceCodeStage))
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=device_flow.slug,
)
return plan.to_redirect(self.request, device_flow)
class OAuthDeviceCodeChallenge(Challenge):

View File

@ -7,8 +7,6 @@ from authentik.core.models import Application
from authentik.flows.models import Flow, in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner
from authentik.flows.stage import SessionEndStage
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.views import PolicyAccessView
@ -37,9 +35,4 @@ class EndSessionView(PolicyAccessView):
},
)
plan.insert_stage(in_memory_stage(SessionEndStage))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=self.flow.slug,
)
return plan.to_redirect(self.request, self.flow)

View File

@ -13,8 +13,6 @@ from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow, in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, FlowPlanner
from authentik.flows.stage import SessionEndStage
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
@ -64,12 +62,7 @@ class SAMLSLOView(PolicyAccessView):
},
)
plan.insert_stage(in_memory_stage(SessionEndStage))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=self.flow.slug,
)
return plan.to_redirect(self.request, self.flow)
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""GET and POST use the same handler, but we can't

View File

@ -13,12 +13,11 @@ from authentik.events.models import Event, EventAction
from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import in_memory_stage
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION, PLAN_CONTEXT_SSO, FlowPlanner
from authentik.flows.views.executor import SESSION_KEY_PLAN, SESSION_KEY_POST
from authentik.lib.utils.urls import redirect_with_qs
from authentik.flows.views.executor import SESSION_KEY_POST
from authentik.lib.views import bad_request_message
from authentik.policies.views import PolicyAccessView
from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
from authentik.providers.saml.views.flows import (
REQUEST_KEY_RELAY_STATE,
@ -74,11 +73,12 @@ class SAMLSSOView(PolicyAccessView):
except FlowNonApplicableException:
raise Http404 from None
plan.append_stage(in_memory_stage(SAMLFlowFinalView))
request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
request.GET,
flow_slug=self.provider.authorization_flow.slug,
return plan.to_redirect(
request,
self.provider.authorization_flow,
allowed_silent_types=(
[SAMLFlowFinalView] if self.provider.sp_binding in [SAMLBindings.REDIRECT] else []
),
)
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:

View File

@ -40,7 +40,6 @@ LANGUAGE_COOKIE_NAME = "authentik_language"
SESSION_COOKIE_NAME = "authentik_session"
SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None)
APPEND_SLASH = False
X_FRAME_OPTIONS = "SAMEORIGIN"
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",

View File

@ -45,8 +45,10 @@ def kerberos_sync_password(sender, user: User, password: str, **_):
continue
with Krb5ConfContext(source):
try:
source.connection().getprinc(user_source_connection.identifier).change_password(
password
kadm = source.connection()
kadm.get_principal(user_source_connection.identifier).change_password(
kadm,
password,
)
except PyKAdminException as exc:
LOGGER.warning("failed to set Kerberos password", exc=exc, source=source)

View File

@ -31,8 +31,7 @@ from authentik.flows.planner import (
FlowPlanner,
)
from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.urls import redirect_with_qs
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET
from authentik.lib.views import bad_request_message
from authentik.providers.saml.utils.encoding import nice64
from authentik.sources.saml.exceptions import MissingSAMLResponse, UnsupportedNameIDFormat
@ -89,12 +88,7 @@ class InitiateView(View):
raise Http404 from None
for stage in stages_to_append:
plan.append_stage(stage)
self.request.session[SESSION_KEY_PLAN] = plan
return redirect_with_qs(
"authentik_core:if-flow",
self.request.GET,
flow_slug=source.pre_authentication_flow.slug,
)
return plan.to_redirect(self.request, source.pre_authentication_flow)
def get(self, request: HttpRequest, source_slug: str) -> HttpResponse:
"""Replies with an XHTML SSO Request."""

View File

@ -332,7 +332,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
serializer = SelectableStageSerializer(
data={
"pk": stage.pk,
"name": getattr(stage, "friendly_name", stage.name),
"name": getattr(stage, "friendly_name", stage.name) or stage.name,
"verbose_name": str(stage._meta.verbose_name)
.replace("Setup Stage", "")
.strip(),

View File

@ -4,6 +4,7 @@ from unittest.mock import MagicMock, patch
from django.test.client import RequestFactory
from django.urls.base import reverse
from django.utils.timezone import now
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowDesignation, FlowStageBinding, NotConfiguredAction
@ -13,6 +14,7 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.generators import generate_id, generate_key
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_static.models import AuthenticatorStaticStage
from authentik.stages.authenticator_totp.models import AuthenticatorTOTPStage, TOTPDigits
from authentik.stages.authenticator_validate.api import AuthenticatorValidateStageSerializer
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses
from authentik.stages.authenticator_validate.stage import PLAN_CONTEXT_DEVICE_CHALLENGES
@ -76,8 +78,8 @@ class AuthenticatorValidateStageTests(FlowTestCase):
conf_stage = AuthenticatorStaticStage.objects.create(
name=generate_id(),
)
conf_stage2 = AuthenticatorStaticStage.objects.create(
name=generate_id(),
conf_stage2 = AuthenticatorTOTPStage.objects.create(
name=generate_id(), digits=TOTPDigits.SIX
)
stage = AuthenticatorValidateStage.objects.create(
name=generate_id(),
@ -153,10 +155,14 @@ class AuthenticatorValidateStageTests(FlowTestCase):
{
"device_class": "static",
"device_uid": "1",
"challenge": {},
"last_used": now(),
},
{
"device_class": "totp",
"device_uid": "2",
"challenge": {},
"last_used": now(),
},
]
session[SESSION_KEY_PLAN] = plan

View File

@ -26,6 +26,7 @@ from authentik.flows.models import FlowDesignation
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, ChallengeStageView
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_KEY_GET
from authentik.lib.avatars import DEFAULT_AVATAR
from authentik.lib.utils.reflection import all_subclasses
from authentik.lib.utils.urls import reverse_with_qs
from authentik.root.middleware import ClientIPMiddleware
@ -76,7 +77,7 @@ class IdentificationChallenge(Challenge):
allow_show_password = BooleanField(default=False)
application_pre = CharField(required=False)
flow_designation = ChoiceField(FlowDesignation.choices)
captcha_stage = CaptchaChallenge(required=False)
captcha_stage = CaptchaChallenge(required=False, allow_null=True)
enroll_url = CharField(required=False)
recovery_url = CharField(required=False)
@ -224,6 +225,8 @@ class IdentificationStageView(ChallengeStageView):
"js_url": current_stage.captcha_stage.js_url,
"site_key": current_stage.captcha_stage.public_key,
"interactive": current_stage.captcha_stage.interactive,
"pending_user": "",
"pending_user_avatar": DEFAULT_AVATAR,
}
if current_stage.captcha_stage
else None

View File

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2024.10.4 Blueprint schema",
"title": "authentik 2024.10.5 Blueprint schema",
"required": [
"version",
"entries"

View File

@ -31,7 +31,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.4}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
restart: unless-stopped
command: server
environment:
@ -49,10 +49,12 @@ services:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
depends_on:
- postgresql
- redis
postgresql:
condition: service_healthy
redis:
condition: service_healthy
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.4}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.10.5}
restart: unless-stopped
command: worker
environment:
@ -76,8 +78,10 @@ services:
env_file:
- .env
depends_on:
- postgresql
- redis
postgresql:
condition: service_healthy
redis:
condition: service_healthy
volumes:
database:

6
go.mod
View File

@ -7,7 +7,7 @@ toolchain go1.23.0
require (
beryju.io/ldap v0.1.0
github.com/coreos/go-oidc/v3 v3.11.0
github.com/getsentry/sentry-go v0.29.1
github.com/getsentry/sentry-go v0.30.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-openapi/runtime v0.28.0
@ -29,10 +29,10 @@ require (
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2024104.1
goauthentik.io/api/v3 v3.2024105.1
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.9.0
golang.org/x/sync v0.10.0
gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
)

12
go.sum
View File

@ -69,8 +69,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA=
github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0=
github.com/getsentry/sentry-go v0.30.0 h1:lWUwDnY7sKHaVIoZ9wYqRHJ5iEmoc0pqcRqFkosKzBo=
github.com/getsentry/sentry-go v0.30.0/go.mod h1:WU9B9/1/sHDqeV8T+3VwwbjeR5MSXs/6aqG3mqZrezA=
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2024104.1 h1:N09HAJ66W965QEYpx6sJzcaQxPsnFykVwkzVjVK/zH0=
goauthentik.io/api/v3 v3.2024104.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
goauthentik.io/api/v3 v3.2024105.1 h1:PxOlStLdM+L80ciVJUWZRhf2VQrDVnNMcv+exeQ/qUA=
goauthentik.io/api/v3 v3.2024105.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -400,8 +400,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2024.10.4"
const VERSION = "2024.10.5"

Binary file not shown.

View File

@ -1,5 +1,5 @@
{
"name": "@goauthentik/authentik",
"version": "2024.10.4",
"version": "2024.10.5",
"private": true
}

308
poetry.lock generated
View File

@ -408,13 +408,13 @@ typeguard = ">=2.13.3,<5.0.0"
[[package]]
name = "aws-cdk-lib"
version = "2.171.1"
version = "2.172.0"
description = "Version 2 of the AWS Cloud Development Kit library"
optional = false
python-versions = "~=3.8"
files = [
{file = "aws_cdk_lib-2.171.1-py3-none-any.whl", hash = "sha256:05da3f0b776db3c083421fd235e6f139441d31e7858e66683fdd6e360b88f949"},
{file = "aws_cdk_lib-2.171.1.tar.gz", hash = "sha256:2b329b926976b03d55bfdfe01ab09886c9f19a337e343686dfe8d23a439f880f"},
{file = "aws_cdk_lib-2.172.0-py3-none-any.whl", hash = "sha256:960b64af8eb272d2bc80d42dab4748863c2021c39dbc543bb6e7bec0fdafa099"},
{file = "aws_cdk_lib-2.172.0.tar.gz", hash = "sha256:4e8cb368256024e2d35874d7ab2e68812177d7990a27b2ceb50c454e8a018533"},
]
[package.dependencies]
@ -1116,73 +1116,73 @@ typeguard = ">=2.13.3,<2.14.0"
[[package]]
name = "coverage"
version = "7.6.8"
version = "7.6.9"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"},
{file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"},
{file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"},
{file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"},
{file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"},
{file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"},
{file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"},
{file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"},
{file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"},
{file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"},
{file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"},
{file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"},
{file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"},
{file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"},
{file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"},
{file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"},
{file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"},
{file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"},
{file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"},
{file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"},
{file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"},
{file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"},
{file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"},
{file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"},
{file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"},
{file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"},
{file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"},
{file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"},
{file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"},
{file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"},
{file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"},
{file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"},
{file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"},
{file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"},
{file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"},
{file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"},
{file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"},
{file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"},
{file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"},
{file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"},
{file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"},
{file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"},
{file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"},
{file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"},
{file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"},
{file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"},
{file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"},
{file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"},
{file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"},
{file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"},
{file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"},
{file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"},
{file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"},
{file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"},
{file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"},
{file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"},
{file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"},
{file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"},
{file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"},
{file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"},
{file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"},
{file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"},
{file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"},
{file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"},
{file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"},
{file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"},
{file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"},
{file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"},
{file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"},
{file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"},
{file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"},
{file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"},
{file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"},
{file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"},
{file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"},
{file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"},
{file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"},
{file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"},
{file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"},
{file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"},
{file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"},
{file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"},
{file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"},
{file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"},
{file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"},
{file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"},
{file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"},
{file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"},
{file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"},
{file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"},
{file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"},
{file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"},
{file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"},
{file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"},
{file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"},
{file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"},
{file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"},
{file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"},
{file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"},
{file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"},
{file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"},
{file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"},
{file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"},
{file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"},
{file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"},
{file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"},
{file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"},
{file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"},
{file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"},
{file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"},
{file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"},
{file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"},
{file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"},
{file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"},
{file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"},
{file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"},
{file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"},
{file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"},
{file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"},
{file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"},
{file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"},
{file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"},
{file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"},
{file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"},
]
[package.extras]
@ -1273,37 +1273,13 @@ tests = ["django", "hypothesis", "pytest", "pytest-asyncio"]
[[package]]
name = "debugpy"
version = "1.8.9"
version = "1.8.10"
description = "An implementation of the Debug Adapter Protocol for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"},
{file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"},
{file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"},
{file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"},
{file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"},
{file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"},
{file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"},
{file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"},
{file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"},
{file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"},
{file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"},
{file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"},
{file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"},
{file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"},
{file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"},
{file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"},
{file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"},
{file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"},
{file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"},
{file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"},
{file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"},
{file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"},
{file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"},
{file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"},
{file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"},
{file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"},
{file = "debugpy-1.8.10-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97aa00af95983887806e06f37e144909d35215d66db74f8b0e9799b4eef40cfd"},
{file = "debugpy-1.8.10.tar.gz", hash = "sha256:ee4ed903cbeb14ee1839549f953af519ffa512598ec987b2051f9c868e2249a8"},
]
[[package]]
@ -1361,13 +1337,13 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
[[package]]
name = "django"
version = "5.0.9"
version = "5.0.10"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
files = [
{file = "Django-5.0.9-py3-none-any.whl", hash = "sha256:f219576ba53be4e83f485130a7283f0efde06a9f2e3a7c3c5180327549f078fa"},
{file = "Django-5.0.9.tar.gz", hash = "sha256:6333870d342329b60174da3a60dbd302e533f3b0bb0971516750e974a99b5a39"},
{file = "Django-5.0.10-py3-none-any.whl", hash = "sha256:c8fab2c553750933c8e7f5f95e5507e138e6acf6c2b4581cb691e70fe3ed747b"},
{file = "Django-5.0.10.tar.gz", hash = "sha256:0f6cbc56cc298b0451d20a5120c6a8731e9073330fb5d84295c23c151a1eb300"},
]
[package.dependencies]
@ -3883,13 +3859,13 @@ files = [
[[package]]
name = "pydantic"
version = "2.10.2"
version = "2.10.3"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"},
{file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"},
{file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"},
{file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"},
]
[package.dependencies]
@ -4260,48 +4236,48 @@ cli = ["click (>=5.0)"]
[[package]]
name = "python-kadmin-rs"
version = "0.3.0"
version = "0.4.0"
description = "Python interface to the Kerberos administration interface (kadm5)"
optional = false
python-versions = "<3.14,>=3.9"
files = [
{file = "python_kadmin_rs-0.3.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:7b40e2e62ba884182a955c8dbf64f9322cd80b5ea904a269c7099abc6a6536de"},
{file = "python_kadmin_rs-0.3.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:055da2fe5e99a0139b85770a182070ee318c1c8a5a2ddbd719aad2eef323d7d3"},
{file = "python_kadmin_rs-0.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:387732550dffa699069412e1181d6c066e4421b47a11287d8c42a4f2fcf3669a"},
{file = "python_kadmin_rs-0.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:4f57335b57e8cf2a6aecd21c27928294d3c408cf82c4f905fc88709126116165"},
{file = "python_kadmin_rs-0.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:800516f5a8b8241adb5e95e11ba2a06b8c99a3219b64b4f28a154fa9abf5f702"},
{file = "python_kadmin_rs-0.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b26cf8994e291f8a4d09e367af8c26de8a4fdf092e094a3ca0200c0300b163a1"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:672866243de0e6e6483450cad2a63b27a6676e64338a9565a472abda5281443c"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:5d436b5662a6e1e4b946c4842d469d86c4ada67f709f5e4dcd2d99222510dea1"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:64003fc8e840d859f343874989d5fee984623324767a954b4c70372e4884970e"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:946868516536ce57b282f038d7a168da9c8e494baae82d735f2713c35d2c4ad9"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03a39b6482b02f0909341baadbacf8d939a24f79f0c10747ad9ec45728f88ab6"},
{file = "python_kadmin_rs-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc75e1bf4df853d361a5b80405d43ffa111f3f1a9e0a680cf341a8e6a5227c49"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:c074a424d51edc74c5d4263ccf546b6f0c3d9bd458ec9ac6a550bfc75d34da1b"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:096fd009beff684691e4b1d9df99f31bef951ecaec29f4cfac01e82e609cd34d"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:340155c21e6be971b558d032f5201058f39dafa34665a32bdcd9d8d4b8e8e2af"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9ea5758fa11e387fab993c1f6af3921386c91c47f487c820a842762b602cee43"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e13d369e1f70cd536d799ab2f653a2c65a6574e671cdd7cbfca863391d8ff903"},
{file = "python_kadmin_rs-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5dc201513e40052d0eab24eb48996776a16a4a9d7c124014d48c09bd66468410"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d58ba4e7a2b80fa5483139a008645a317689727a0c24a5b080ef531318f0ef4e"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:426328d2656f8595df9e1463da08521021a73d423ff6b24fdc6d32fd44d5a3a7"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:0760b03075ecdb1ec8f19919c256b4a1e3cb15f451d1face8ceafb1b88d0449f"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1c670aa445da67f5168be71a440c6f5e4de5392c123ff863a60fc2ea55e56b7b"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63e5fbc0d1104da050ffedb29a947222fedfc07d762719fd34355feb8d4bc64b"},
{file = "python_kadmin_rs-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b76bc37d51ecf1dfaa58e9f0134c2c49a75827e5035ab7bcbb228c8188049a24"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:098fc8d748974682c89888708affcca6b438504e268869c13aa62e67cb4b0414"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:cb4561a105f7cfe66d53d580baf45461bcfe682ef39c601aecbf608d57bc417c"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:a3632d9bcbaf36fcdb7cf89c5233c05185f51befa617dc74064901c3b4897cd0"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2243eb7482e6f2f3f15caeae42f13e2ca842e78d52a0b3d60fb72c488d2b8143"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e6c588c673ecf99437acdbe3d8ee0771be354ce857515f96a32c401174144029"},
{file = "python_kadmin_rs-0.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb7e497bfe8fdc39383c303050eadaf7a891832ed83545b0b7cfba71099bdddf"},
{file = "python_kadmin_rs-0.3.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:4b6489498e6cd08b84771891678a9dba05a4917104e3ef1f6d7170e7a6fea0a3"},
{file = "python_kadmin_rs-0.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ae0bb5f5d4527cdf163efae7726579b0018849087bf79ca7225f0a4bb753fbbf"},
{file = "python_kadmin_rs-0.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3a3c0cfbe8569b2cf40df0bbbeca125de47f35c98ea16620a34fb5eaac958794"},
{file = "python_kadmin_rs-0.3.0-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:aba9b60842dbf7bc82a653e89132118f663d7c4239238e6e8419c1983a01e32e"},
{file = "python_kadmin_rs-0.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2760ff7ff68868155c94bfb62bd7972f8d0604e066eca2a69b82311b2807f5b5"},
{file = "python_kadmin_rs-0.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:53e74326e05f8651917fab28d56f54e8e25644c87a8068b5c9af45e202fe619f"},
{file = "python_kadmin_rs-0.3.0.tar.gz", hash = "sha256:2e39a9069ade69166e92968871dc84a9990d1c1bf8af11cc146ac38dac65f4e8"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b5a5abda2c60961c1d456c920dd3a3053e615a6f1f5703606953be8dfdddef2a"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:57004e7aa52d95a76b0c6d920526f68b45206c51d8d8520d94511727c7ccbad0"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:66f01443b6376494f67d727663600a413a701852a60c724a3cd728758455f59c"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2af5a50554753ba62ebc979b7767b43e072cff5b56dc0a1f09970fa9105cf55a"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1720b3b9dc156be08e36b7f3492431d2b475b3ecbfa403d73d6e1fcc5ac70bc4"},
{file = "python_kadmin_rs-0.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66a64d615d28dbf17ad8822d75f6a4685f7db7ddef9ad9d69053dcfab592e4ec"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:56ce2b57fbb3b0e7d0e69bd9ce3e7a165ed018ac4c4d60b259f50e68a6a3bb00"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:1aabecd407afd70fca21208f35ea6d2101fb27922e96c5ceed7fcaa6c44359b0"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:e53eb9914eb6542618ec5da67c51e943eb724f76f186d88ae591bd8fde01345a"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c51115155ff1001ab3a0826a3de753927ea1373828e5432bc0eede4ec88c5c72"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:547223f156baa8ef1236c7b3a55bc13506beada6147679f4a73dd1de5e809d30"},
{file = "python_kadmin_rs-0.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:951ca2b9b3019cf82c5e1882d1cec6e28bbf2d900d2b8022aac23a3e65a4ca7d"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b1c1a0b63ec3bd1a023feb094e1c6a93202237416d0783d4677be2b858fe6154"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b27c16187dd24b820c966f03f889c140d0a55f547158fdc5bc2ecb4eb7e94fbe"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fab810574fd54b715806104400a5c105879005597bc043469d506cb8e1e633c4"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:377ffa81264b115fafd2b4a83aab990a138a3684b90a133bc3a6c4081829c358"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7b9a3909592404ac0483b3a5d584466198b5e17e370be3e221ff19c4cec97ce4"},
{file = "python_kadmin_rs-0.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e6050fdaa638585046b8579867d3540f99efbf24dc10715ac05bae6ca9bbbffd"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e36c868960619ed4df0e69f53ff9458f661c1a5fbc627554cc7777231e9e69bd"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:3f7692eb90ddacc353a5ed3d53fe0bc62df4132b30158e1c9a2bf24340a6929a"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7a551f8010e47413513cc19e0001dfed9178f5de509c4590b02584e0387df55c"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e8623866a0324823af5edc2da6a6e90cb8a0d2ecbeb80f9a04014cc18f1c182f"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:223807b9108723d4f47d3243f6256f4026be0ea7ccbb356807d97a469a8bc628"},
{file = "python_kadmin_rs-0.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e7b8f6a2b183c862b94462251537d508332c82d2c4dec1699875245041c4a684"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:dee6325628edc33eaf217268b521b0923f519fdb7f5ac81dcfb97c9574fb3599"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:59b5db1d0381fbfb0b9ff2f79949abae6c645ccbd7c8c72a9b932fc0eab1d9b0"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:45fd65c49e0c64968d11eb7f6b93a9a09788967ca667e554f35fea467ea67f1e"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:fc49b75be7d032f5a37a53b777267b81070220b9d14777374f159c5b1f64686a"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fafb2e57fbc82e27c26c5450669846e02afbf6b4065127c4396fa2c21ec31c42"},
{file = "python_kadmin_rs-0.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:59b622b7396922748b2463ad0a682b6f6a6887f3eee720eeda8a57bed6370555"},
{file = "python_kadmin_rs-0.4.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:40ce8ff6dcc1bd82f34e7aca611e299a4ca51f28e5cb8772ba7d38532c9564d9"},
{file = "python_kadmin_rs-0.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:1ef8030f282bdcbb2a771699e238b0f555336ddd626d4562ef3e2e17abc31c3b"},
{file = "python_kadmin_rs-0.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:617292305b3fde5e6b009b70ae8fd6cc5c7a962732558cffbdc27fca157ce574"},
{file = "python_kadmin_rs-0.4.0-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:6fe1d0a03d0a0a75296902ad95b6639372eed93d16422f33572d23b0b144ce64"},
{file = "python_kadmin_rs-0.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7fef94fe96687b9c6eb9bf670afda91f24c62fb2bc2f80394e0f2f31474494a5"},
{file = "python_kadmin_rs-0.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:627dff4aa5c222fc83cbb5312362bf80c68b515afd7027b27d763916e0adeb39"},
{file = "python_kadmin_rs-0.4.0.tar.gz", hash = "sha256:d32befeaa68dbaac077b565f5a47a23cba6e142190c0d521e595b30de8587efa"},
]
[[package]]
@ -4632,29 +4608,29 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
version = "0.8.1"
version = "0.8.2"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.8.1-py3-none-linux_armv6l.whl", hash = "sha256:fae0805bd514066f20309f6742f6ee7904a773eb9e6c17c45d6b1600ca65c9b5"},
{file = "ruff-0.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b8a4f7385c2285c30f34b200ca5511fcc865f17578383db154e098150ce0a087"},
{file = "ruff-0.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd054486da0c53e41e0086e1730eb77d1f698154f910e0cd9e0d64274979a209"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2029b8c22da147c50ae577e621a5bfbc5d1fed75d86af53643d7a7aee1d23871"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2666520828dee7dfc7e47ee4ea0d928f40de72056d929a7c5292d95071d881d1"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:333c57013ef8c97a53892aa56042831c372e0bb1785ab7026187b7abd0135ad5"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:288326162804f34088ac007139488dcb43de590a5ccfec3166396530b58fb89d"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b12c39b9448632284561cbf4191aa1b005882acbc81900ffa9f9f471c8ff7e26"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:364e6674450cbac8e998f7b30639040c99d81dfb5bbc6dfad69bc7a8f916b3d1"},
{file = "ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c"},
{file = "ruff-0.8.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b2f2f7a7e7648a2bfe6ead4e0a16745db956da0e3a231ad443d2a66a105c04fa"},
{file = "ruff-0.8.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:adf314fc458374c25c5c4a4a9270c3e8a6a807b1bec018cfa2813d6546215540"},
{file = "ruff-0.8.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a885d68342a231b5ba4d30b8c6e1b1ee3a65cf37e3d29b3c74069cdf1ee1e3c9"},
{file = "ruff-0.8.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d2c16e3508c8cc73e96aa5127d0df8913d2290098f776416a4b157657bee44c5"},
{file = "ruff-0.8.1-py3-none-win32.whl", hash = "sha256:93335cd7c0eaedb44882d75a7acb7df4b77cd7cd0d2255c93b28791716e81790"},
{file = "ruff-0.8.1-py3-none-win_amd64.whl", hash = "sha256:2954cdbe8dfd8ab359d4a30cd971b589d335a44d444b6ca2cb3d1da21b75e4b6"},
{file = "ruff-0.8.1-py3-none-win_arm64.whl", hash = "sha256:55873cc1a473e5ac129d15eccb3c008c096b94809d693fc7053f588b67822737"},
{file = "ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f"},
{file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"},
{file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"},
{file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"},
{file = "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"},
{file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"},
{file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"},
{file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"},
{file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"},
{file = "ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"},
{file = "ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"},
{file = "ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"},
{file = "ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"},
]
[[package]]
@ -4712,13 +4688,13 @@ websocket-client = ">=1.8,<2.0"
[[package]]
name = "sentry-sdk"
version = "2.19.0"
version = "2.19.2"
description = "Python client for Sentry (https://sentry.io)"
optional = false
python-versions = ">=3.6"
files = [
{file = "sentry_sdk-2.19.0-py2.py3-none-any.whl", hash = "sha256:7b0b3b709dee051337244a09a30dbf6e95afe0d34a1f8b430d45e0982a7c125b"},
{file = "sentry_sdk-2.19.0.tar.gz", hash = "sha256:ee4a4d2ae8bfe3cac012dcf3e4607975904c137e1738116549fc3dbbb6ff0e36"},
{file = "sentry_sdk-2.19.2-py2.py3-none-any.whl", hash = "sha256:ebdc08228b4d131128e568d696c210d846e5b9d70aa0327dec6b1272d9d40b84"},
{file = "sentry_sdk-2.19.2.tar.gz", hash = "sha256:467df6e126ba242d39952375dd816fbee0f217d119bf454a8ce74cf1e7909e8d"},
]
[package.dependencies]
@ -5089,13 +5065,13 @@ wsproto = ">=0.14"
[[package]]
name = "twilio"
version = "9.3.7"
version = "9.3.8"
description = "Twilio API client and TwiML generator"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "twilio-9.3.7-py2.py3-none-any.whl", hash = "sha256:7d5d05140530f0eaf60d6a810c88da443cb2e6aad18a0830e4cb0ccd7b338d30"},
{file = "twilio-9.3.7.tar.gz", hash = "sha256:0f747f6c29b0ddc50a55e51739abb28c83b83d97917b02e784119058a310db05"},
{file = "twilio-9.3.8-py2.py3-none-any.whl", hash = "sha256:fce1f629295285d583dbe1d615f114a77aab25a654ba569bb18d304d31e9ca3b"},
{file = "twilio-9.3.8.tar.gz", hash = "sha256:93a80639db711e58915cfdf772da6274b005ef86f5d2f6092433cb3d53a25303"},
]
[package.dependencies]
@ -5480,13 +5456,13 @@ files = [
[[package]]
name = "webauthn"
version = "2.3.0"
version = "2.4.0"
description = "Pythonic WebAuthn"
optional = false
python-versions = "*"
files = [
{file = "webauthn-2.3.0-py3-none-any.whl", hash = "sha256:872668fd8f32e256e76e4251e04eb0737e77e0760b1db3912af11346cbacef9e"},
{file = "webauthn-2.3.0.tar.gz", hash = "sha256:79fca835027d3b39290bfd175d09ca7a2bd6e12163790feb6d9c0b746e4c2ede"},
{file = "webauthn-2.4.0-py3-none-any.whl", hash = "sha256:2bf59646e1ad2aed113d16a1ca90196b45f1c4d160964d6271a181e60d0d03b1"},
{file = "webauthn-2.4.0.tar.gz", hash = "sha256:9bb4f95c5d2377f9e1abd156ca5a23cbb5def69ef1ed60a7ab70028cc68b741e"},
]
[package.dependencies]
@ -5936,4 +5912,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "~3.12"
content-hash = "b4eeab86e953e9c6d7e9336003e258668e43f58aeb360b15f56f8d4874fea5c3"
content-hash = "f6e8316415a23b165130d63a7ea311b257f65f5478ad85b0d38ac72fb89bc1c4"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "authentik"
version = "2024.10.4"
version = "2024.10.5"
description = ""
authors = ["authentik Team <hello@goauthentik.io>"]
@ -131,7 +131,7 @@ pydantic-scim = "*"
pyjwt = "*"
pyrad = "*"
python = "~3.12"
python-kadmin-rs = "0.3.0"
python-kadmin-rs = "0.4.0"
pyyaml = "*"
requests-oauthlib = "*"
scim2-filter-parser = "*"

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2024.10.4
version: 2024.10.5
description: Making authentication simple.
contact:
email: hello@goauthentik.io

View File

@ -1,84 +0,0 @@
// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
//
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
// import CSS modules.
//
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
// and we'll have one unified way of doing this. I can only hope.
const rawCssImportMaps = [
'import AKGlobal from "../../../common/styles/authentik.css";',
'import AKGlobal from "../../common/styles/authentik.css";',
'import AKGlobal from "../common/styles/authentik.css";',
'import AKGlobal from "@goauthentik/common/styles/authentik.css";',
'import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";',
'import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";',
'import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";',
'import PFBackdrop from "@patternfly/patternfly/components/Backdrop/backdrop.css";',
'import PFBackgroundImage from "@patternfly/patternfly/components/BackgroundImage/background-image.css";',
'import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";',
'import PFBase from "@patternfly/patternfly/patternfly-base.css";',
'import PFBrand from "@patternfly/patternfly/components/Brand/brand.css";',
'import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";',
'import PFButton from "@patternfly/patternfly/components/Button/button.css";',
'import PFCard from "@patternfly/patternfly/components/Card/card.css";',
'import PFCheck from "@patternfly/patternfly/components/Check/check.css";',
'import PFChip from "@patternfly/patternfly/components/Chip/chip.css";',
'import PFChipGroup from "@patternfly/patternfly/components/ChipGroup/chip-group.css";',
'import PFContent from "@patternfly/patternfly/components/Content/content.css";',
'import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";',
'import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";',
'import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";',
'import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";',
'import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";',
'import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";',
'import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css";',
'import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";',
'import PFExpandableSection from "@patternfly/patternfly/components/ExpandableSection/expandable-section.css";',
'import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";',
'import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";',
'import PFForm from "@patternfly/patternfly/components/Form/form.css";',
'import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";',
'import PFGallery from "@patternfly/patternfly/layouts/Gallery/gallery.css";',
'import PFGlobal from "@patternfly/patternfly/patternfly-base.css";',
'import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";',
'import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";',
'import PFLabel from "@patternfly/patternfly/components/Label/label.css";',
'import PFList from "@patternfly/patternfly/components/List/list.css";',
'import PFLogin from "@patternfly/patternfly/components/Login/login.css";',
'import PFModalBox from "@patternfly/patternfly/components/ModalBox/modal-box.css";',
'import PFNav from "@patternfly/patternfly/components/Nav/nav.css";',
'import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css";',
'import PFNotificationDrawer from "@patternfly/patternfly/components/NotificationDrawer/notification-drawer.css";',
'import PFPage from "@patternfly/patternfly/components/Page/page.css";',
'import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";',
'import PFProgress from "@patternfly/patternfly/components/Progress/progress.css";',
'import PFProgressStepper from "@patternfly/patternfly/components/ProgressStepper/progress-stepper.css";',
'import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";',
'import PFSelect from "@patternfly/patternfly/components/Select/select.css";',
'import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";',
'import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";',
'import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";',
'import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";',
'import PFSplit from "@patternfly/patternfly/layouts/Split/split.css";',
'import PFStack from "@patternfly/patternfly/layouts/Stack/stack.css";',
'import PFSwitch from "@patternfly/patternfly/components/Switch/switch.css";',
'import PFTable from "@patternfly/patternfly/components/Table/table.css";',
'import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css";',
'import PFTitle from "@patternfly/patternfly/components/Title/title.css";',
'import PFToggleGroup from "@patternfly/patternfly/components/ToggleGroup/toggle-group.css";',
'import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";',
'import PFTreeView from "@patternfly/patternfly/components/TreeView/tree-view.css";',
'import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";',
'import ThemeDark from "@goauthentik/common/styles/theme-dark.css";',
'import styles from "./LibraryPageImpl.css";',
];
const cssImportMaps = rawCssImportMaps.reduce(
(acc, line) => ({ ...acc, [line]: line.replace(/\.css/, ".css?inline") }),
{},
);
export { cssImportMaps };
export default cssImportMaps;

View File

@ -5,11 +5,19 @@ import modify from "rollup-plugin-modify";
import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths";
import { cssImportMaps } from "./css-import-maps";
export const isProdBuild = process.env.NODE_ENV === "production";
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
const importInlinePatterns = [
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
'import PF.+ from "@patternfly/patternfly/\\S+\\.css',
'import ThemeDark from "@goauthentik/common/styles/theme-dark\\.css',
'import styles from "\\./LibraryPageImpl\\.css',
];
const importInlineRegexp = new RegExp(importInlinePatterns.map((a) => `(${a})`).join("|"));
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
@ -43,7 +51,12 @@ const config: StorybookConfig = {
return {
...config,
plugins: [
modify(cssImportMaps),
modify({
find: importInlineRegexp,
replace: (match: RegExpMatchArray) => {
return `${match}?inline`;
},
}),
replace({
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development",

8
web/package-lock.json generated
View File

@ -23,7 +23,7 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.10.4-1733219849",
"@goauthentik/api": "^2024.10.5-1733854821",
"@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
@ -1775,9 +1775,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2024.10.4-1733219849",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.10.4-1733219849.tgz",
"integrity": "sha512-Bls5D7PjtOytn2NqWUQEzKxeBql+kwwOWvBR+Dvo9fj7j6J5Sj5O/cIwGlKqN9q6p3ynSarstPv5YlLvYvPOGw=="
"version": "2024.10.5-1733854821",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2024.10.5-1733854821.tgz",
"integrity": "sha512-WLZxl56j0b007b/Mv3xhWZS5oWCOeCDnn1fq4I3xN/Esm76uuAOizXN/Cpv6QMLzcwoMKTE6d5L4GUDIy3jwZQ=="
},
"node_modules/@goauthentik/web": {
"resolved": "",

View File

@ -11,7 +11,7 @@
"@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.5.7",
"@fortawesome/fontawesome-free": "^6.6.0",
"@goauthentik/api": "^2024.10.4-1733219849",
"@goauthentik/api": "^2024.10.5-1733854821",
"@lit-labs/ssr": "^3.2.2",
"@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2",
@ -133,8 +133,8 @@
"pseudolocalize": "wireit",
"storybook": "storybook dev -p 6006",
"storybook:build": "wireit",
"storybook:build-import-map": "wireit",
"test": "wireit",
"test:e2e": "wireit",
"test:e2e:watch": "wireit",
"test:watch": "wireit",
"tsc": "wireit",
@ -316,9 +316,6 @@
"NODE_OPTIONS": "--max_old_space_size=8192"
}
},
"storybook:build-import-map": {
"command": "node scripts/build-storybook-import-maps.mjs"
},
"test": {
"command": "wdio ./wdio.conf.ts --logLevel=warn",
"env": {
@ -326,6 +323,16 @@
"TS_NODE_PROJECT": "tsconfig.test.json"
}
},
"test:e2e": {
"command": "wdio run ./tests/wdio.conf.ts",
"dependencies": [
"build"
],
"env": {
"CI": "true",
"TS_NODE_PROJECT": "./tests/tsconfig.test.json"
}
},
"test:e2e:watch": {
"command": "wdio run ./tests/wdio.conf.ts",
"dependencies": [

View File

@ -1,91 +0,0 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = fileURLToPath(new URL(".", import.meta.url));
function* walkFilesystem(dir) {
const openeddir = fs.opendirSync(dir);
if (!openeddir) {
return;
}
let d;
while ((d = openeddir?.readSync())) {
if (!d) {
break;
}
const entry = path.join(dir, d.name);
if (d.isDirectory()) yield* walkFilesystem(entry);
else if (d.isFile()) yield entry;
}
openeddir.close();
}
const import_re = /^(import \w+ from .*\.css)";/;
function extractImportLinesFromFile(path) {
const source = fs.readFileSync(path, { encoding: "utf8", flag: "r" });
const lines = source?.split("\n") ?? [];
return lines.filter((l) => import_re.test(l));
}
function createOneImportLine(line) {
const importMatch = import_re.exec(line);
if (!importMatch) {
throw new Error("How did an unmatchable line get here?");
}
const importContent = importMatch[1];
if (!importContent) {
throw new Error("How did an unmatchable line get here!?");
}
return ` '${importContent}";',`;
}
const isSourceFile = /\.ts$/;
function getTheSourceFiles() {
return Array.from(walkFilesystem(path.join(__dirname, "..", "src"))).filter((path) =>
isSourceFile.test(path),
);
}
function getTheImportLines(importPaths) {
const importLines = importPaths.reduce(
(acc, path) => [...acc, extractImportLinesFromFile(path)].flat(),
[],
);
const uniqueImportLines = new Set(importLines);
const sortedImportLines = Array.from(uniqueImportLines.keys());
sortedImportLines.sort();
return sortedImportLines;
}
const importPaths = getTheSourceFiles();
const importLines = getTheImportLines(importPaths);
const outputFile = `// THIS IS A GENERATED FILE. DO NOT EDIT BY HAND.
//
// This file is generated by the build-storybook-import-maps script in the UI's base directory.
// This is a *hack* to work around an inconsistency in the way rollup, vite, and storybook
// import CSS modules.
//
// Sometime around 2030 or so, the Javascript community may finally get its collective act together
// and we'll have one unified way of doing this. I can only hope.
const rawCssImportMaps = [
${importLines.map(createOneImportLine).join("\n")}
];
const cssImportMaps = rawCssImportMaps.reduce(
(acc, line) => ({ ...acc, [line]: line.replace(/\\.css/, ".css?inline") }),
{},
);
export { cssImportMaps };
export default cssImportMaps;
`;
fs.writeFileSync(path.join(__dirname, "..", ".storybook", "css-import-maps.ts"), outputFile, {
encoding: "utf8",
flag: "w",
});

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2024.10.4";
export const VERSION = "2024.10.5";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -0,0 +1,41 @@
// This is a more modern way to handle disconnecting listeners on demand.
// example usage:
/*
export class MyElement extends LitElement {
this.listenerController = new ListenerController();
connectedCallback() {
super.connectedCallback();
window.addEventListener("event-1", handler1, { signal: this.listenerController.signal });
window.addEventListener("event-2", handler2, { signal: this.listenerController.signal });
window.addEventListener("event-3", handler3, { signal: this.listenerController.signal });
}
disconnectedCallback() {
// This will disconnect *all* the event listeners at once, and resets the listenerController,
// releasing the memory used for the signal as well. No more trying to map all the
// `addEventListener` to `removeEventListener` tediousness!
this.listenerController.abort();
super.disconnectedCallback();
}
}
*/
export class ListenerController {
listenerController?: AbortController;
get signal() {
if (!this.listenerController) {
this.listenerController = new AbortController();
}
return this.listenerController.signal;
}
abort() {
this.listenerController?.abort();
this.listenerController = undefined;
}
}

View File

@ -1,14 +1,18 @@
///<reference types="@hcaptcha/types"/>
import { renderStatic } from "@goauthentik/common/purify";
import "@goauthentik/elements/EmptyState";
import { akEmptyState } from "@goauthentik/elements/EmptyState";
import { bound } from "@goauthentik/elements/decorators/bound";
import "@goauthentik/elements/forms/FormElement";
import { ListenerController } from "@goauthentik/elements/utils/listenerController.js";
import { randomId } from "@goauthentik/elements/utils/randomId";
import "@goauthentik/flow/FormStatic";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { P, match } from "ts-pattern";
import type { TurnstileObject } from "turnstile-types";
import { msg } from "@lit/localize";
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
@ -23,8 +27,72 @@ import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "@goauthentik/
interface TurnstileWindow extends Window {
turnstile: TurnstileObject;
}
type TokenHandler = (token: string) => void;
type Dims = { height: number };
type IframeCaptchaMessage = {
source?: string;
context?: string;
message: "captcha";
token: string;
};
type IframeResizeMessage = {
source?: string;
context?: string;
message: "resize";
size: Dims;
};
type IframeMessageEvent = MessageEvent<IframeCaptchaMessage | IframeResizeMessage>;
type CaptchaHandler = {
name: string;
interactive: () => Promise<unknown>;
execute: () => Promise<unknown>;
};
// A container iframe for a hosted Captcha, with an event emitter to monitor when the Captcha forces
// a resize. Because the Captcha is itself in an iframe, the reported height is often off by some
// margin, so adding 2rem of height to our container adds padding and prevents scroll bars or hidden
// rendering.
const iframeTemplate = (captchaElement: TemplateResult, challengeUrl: string) =>
html`<!doctype html>
<head>
<html>
<body style="display:flex;flex-direction:row;justify-content:center;">
${captchaElement}
<script>
new ResizeObserver((entries) => {
const height =
document.body.offsetHeight +
parseFloat(getComputedStyle(document.body).fontSize) * 2;
window.parent.postMessage({
message: "resize",
source: "goauthentik.io",
context: "flow-executor",
size: { height },
});
}).observe(document.querySelector(".ak-captcha-container"));
</script>
<script src=${challengeUrl}></script>
<script>
function callback(token) {
window.parent.postMessage({
message: "captcha",
source: "goauthentik.io",
context: "flow-executor",
token: token,
});
}
</script>
</body>
</html>
</head>`;
@customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> {
static get styles(): CSSResult[] {
@ -37,26 +105,12 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
css`
iframe {
width: 100%;
height: 73px; /* tmp */
height: 0;
}
`,
];
}
handlers = [this.handleGReCaptcha, this.handleHCaptcha, this.handleTurnstile];
@state()
error?: string;
@state()
captchaFrame: HTMLIFrameElement;
@state()
captchaDocumentContainer: HTMLDivElement;
@state()
scriptElement?: HTMLScriptElement;
@property({ type: Boolean })
embedded = false;
@ -65,209 +119,177 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
this.host.submit({ component: "ak-stage-captcha", token });
};
constructor() {
super();
this.captchaFrame = document.createElement("iframe");
this.captchaFrame.src = "about:blank";
this.captchaFrame.id = `ak-captcha-${randomId()}`;
@state()
error?: string;
this.captchaDocumentContainer = document.createElement("div");
this.captchaDocumentContainer.id = `ak-captcha-${randomId()}`;
this.messageCallback = this.messageCallback.bind(this);
}
handlers: CaptchaHandler[] = [
{
name: "grecaptcha",
interactive: this.renderGReCaptchaFrame,
execute: this.executeGReCaptcha,
},
{
name: "hcaptcha",
interactive: this.renderHCaptchaFrame,
execute: this.executeHCaptcha,
},
{
name: "turnstile",
interactive: this.renderTurnstileFrame,
execute: this.executeTurnstile,
},
];
_captchaFrame?: HTMLIFrameElement;
_captchaDocumentContainer?: HTMLDivElement;
_listenController = new ListenerController();
connectedCallback(): void {
super.connectedCallback();
window.addEventListener("message", this.messageCallback);
window.addEventListener("message", this.onIframeMessage, {
signal: this._listenController.signal,
});
}
disconnectedCallback(): void {
super.disconnectedCallback();
window.removeEventListener("message", this.messageCallback);
if (!this.challenge.interactive) {
document.body.removeChild(this.captchaDocumentContainer);
this._listenController.abort();
if (!this.challenge?.interactive) {
if (document.body.contains(this.captchaDocumentContainer)) {
document.body.removeChild(this.captchaDocumentContainer);
}
}
super.disconnectedCallback();
}
messageCallback(
ev: MessageEvent<{
source?: string;
context?: string;
message: string;
token: string;
}>,
) {
const msg = ev.data;
if (msg.source !== "goauthentik.io" || msg.context !== "flow-executor") {
return;
get captchaDocumentContainer() {
if (this._captchaDocumentContainer) {
return this._captchaDocumentContainer;
}
if (msg.message !== "captcha") {
return;
this._captchaDocumentContainer = document.createElement("div");
this._captchaDocumentContainer.id = `ak-captcha-${randomId()}`;
return this._captchaDocumentContainer;
}
get captchaFrame() {
if (this._captchaFrame) {
return this._captchaFrame;
}
this.onTokenChange(msg.token);
this._captchaFrame = document.createElement("iframe");
this._captchaFrame.src = "about:blank";
this._captchaFrame.id = `ak-captcha-${randomId()}`;
return this._captchaFrame;
}
onFrameResize({ height }: Dims) {
this.captchaFrame.style.height = `${height}px`;
}
// ADR: Did not to put anything into `otherwise` or `exhaustive` here because iframe messages
// that were not of interest to us also weren't necessarily corrupt or suspicious. For example,
// during testing Storybook throws a lot of cross-iframe messages that we don't care about.
@bound
onIframeMessage({ data }: IframeMessageEvent) {
match(data)
.with(
{ source: "goauthentik.io", context: "flow-executor", message: "captcha" },
({ token }) => this.onTokenChange(token),
)
.with(
{ source: "goauthentik.io", context: "flow-executor", message: "resize" },
({ size }) => this.onFrameResize(size),
)
.with(
{ source: "goauthentik.io", context: "flow-executor", message: P.any },
({ message }) => {
console.debug(`authentik/stages/captcha: Unknown message: ${message}`);
},
)
.otherwise(() => {});
}
async renderGReCaptchaFrame() {
this.renderFrame(
html`<div
class="g-recaptcha ak-captcha-container"
data-sitekey="${this.challenge.siteKey}"
data-callback="callback"
></div>`,
);
}
async executeGReCaptcha() {
return grecaptcha.ready(() => {
grecaptcha.execute(
grecaptcha.render(this.captchaDocumentContainer, {
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
size: "invisible",
}),
);
});
}
async renderHCaptchaFrame() {
this.renderFrame(
html`<div
class="h-captcha ak-captcha-container"
data-sitekey="${this.challenge.siteKey}"
data-theme="${this.activeTheme ? this.activeTheme : "light"}"
data-callback="callback"
></div> `,
);
}
async executeHCaptcha() {
return hcaptcha.execute(
hcaptcha.render(this.captchaDocumentContainer, {
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
size: "invisible",
}),
);
}
async renderTurnstileFrame() {
this.renderFrame(
html`<div
class="cf-turnstile ak-captcha-container"
data-sitekey="${this.challenge.siteKey}"
data-callback="callback"
></div>`,
);
}
async executeTurnstile() {
return (window as unknown as TurnstileWindow).turnstile.render(
this.captchaDocumentContainer,
{
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
},
);
}
async renderFrame(captchaElement: TemplateResult) {
this.captchaFrame.contentWindow?.document.open();
this.captchaFrame.contentWindow?.document.write(
await renderStatic(
html`<!doctype html>
<html>
<body style="display:flex;flex-direction:row;justify-content:center;">
${captchaElement}
<script src=${this.challenge.jsUrl}></script>
<script>
function callback(token) {
window.parent.postMessage({
message: "captcha",
source: "goauthentik.io",
context: "flow-executor",
token: token,
});
}
</script>
</body>
</html>`,
),
await renderStatic(iframeTemplate(captchaElement, this.challenge.jsUrl)),
);
this.captchaFrame.contentWindow?.document.close();
}
updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has("challenge") && this.challenge !== undefined) {
this.scriptElement = document.createElement("script");
this.scriptElement.src = this.challenge.jsUrl;
this.scriptElement.async = true;
this.scriptElement.defer = true;
this.scriptElement.dataset.akCaptchaScript = "true";
this.scriptElement.onload = async () => {
console.debug("authentik/stages/captcha: script loaded");
let found = false;
let lastError = undefined;
this.handlers.forEach(async (handler) => {
let handlerFound = false;
try {
console.debug(`authentik/stages/captcha[${handler.name}]: trying handler`);
handlerFound = await handler.apply(this);
if (handlerFound) {
console.debug(
`authentik/stages/captcha[${handler.name}]: handler succeeded`,
);
found = true;
}
} catch (exc) {
console.debug(
`authentik/stages/captcha[${handler.name}]: handler failed: ${exc}`,
);
if (handlerFound) {
lastError = exc;
}
}
});
if (!found && lastError) {
this.error = (lastError as Error).toString();
}
};
document.head
.querySelectorAll("[data-ak-captcha-script=true]")
.forEach((el) => el.remove());
document.head.appendChild(this.scriptElement);
if (!this.challenge.interactive) {
document.body.appendChild(this.captchaDocumentContainer);
}
}
}
async handleGReCaptcha(): Promise<boolean> {
if (!Object.hasOwn(window, "grecaptcha")) {
return false;
}
if (this.challenge.interactive) {
this.renderFrame(
html`<div
class="g-recaptcha"
data-sitekey="${this.challenge.siteKey}"
data-callback="callback"
></div>`,
);
} else {
grecaptcha.ready(() => {
const captchaId = grecaptcha.render(this.captchaDocumentContainer, {
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
size: "invisible",
});
grecaptcha.execute(captchaId);
});
}
return true;
}
async handleHCaptcha(): Promise<boolean> {
if (!Object.hasOwn(window, "hcaptcha")) {
return false;
}
if (this.challenge.interactive) {
this.renderFrame(
html`<div
class="h-captcha"
data-sitekey="${this.challenge.siteKey}"
data-theme="${this.activeTheme ? this.activeTheme : "light"}"
data-callback="callback"
></div> `,
);
} else {
const captchaId = hcaptcha.render(this.captchaDocumentContainer, {
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
size: "invisible",
});
hcaptcha.execute(captchaId);
}
return true;
}
async handleTurnstile(): Promise<boolean> {
if (!Object.hasOwn(window, "turnstile")) {
return false;
}
if (this.challenge.interactive) {
this.renderFrame(
html`<div
class="cf-turnstile"
data-sitekey="${this.challenge.siteKey}"
data-callback="callback"
></div>`,
);
} else {
(window as unknown as TurnstileWindow).turnstile.render(this.captchaDocumentContainer, {
sitekey: this.challenge.siteKey,
callback: this.onTokenChange,
});
}
return true;
}
renderBody() {
if (this.error) {
return html`<ak-empty-state icon="fa-times" header=${this.error}> </ak-empty-state>`;
}
if (this.challenge.interactive) {
return html`${this.captchaFrame}`;
}
return html`<ak-empty-state loading header=${msg("Verifying...")}></ak-empty-state>`;
// [hasError, isInteractive]
// prettier-ignore
return match([Boolean(this.error), Boolean(this.challenge?.interactive)])
.with([true, P.any], () => akEmptyState({ icon: "fa-times", header: this.error }))
.with([false, true], () => html`${this.captchaFrame}`)
.with([false, false], () => akEmptyState({ loading: true, header: msg("Verifying...") }))
.exhaustive();
}
render() {
if (this.embedded) {
if (!this.challenge.interactive) {
return html``;
}
return this.renderBody();
}
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
renderMain() {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
@ -291,6 +313,63 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
}
render() {
// [isEmbedded, hasChallenge, isInteractive]
// prettier-ignore
return match([this.embedded, Boolean(this.challenge), Boolean(this.challenge?.interactive)])
.with([true, false, P.any], () => nothing)
.with([true, true, false], () => nothing)
.with([true, true, true], () => this.renderBody())
.with([false, false, P.any], () => akEmptyState({ loading: true }))
.with([false, true, P.any], () => this.renderMain())
.exhaustive();
}
updated(changedProperties: PropertyValues<this>) {
if (!(changedProperties.has("challenge") && this.challenge !== undefined)) {
return;
}
const attachCaptcha = async () => {
console.debug("authentik/stages/captcha: script loaded");
const handlers = this.handlers.filter(({ name }) => Object.hasOwn(window, name));
let lastError = undefined;
let found = false;
for (const { name, interactive, execute } of handlers) {
console.debug(`authentik/stages/captcha: trying handler ${name}`);
try {
const runner = this.challenge.interactive ? interactive : execute;
await runner.apply(this);
console.debug(`authentik/stages/captcha[${name}]: handler succeeded`);
found = true;
break;
} catch (exc) {
console.debug(`authentik/stages/captcha[${name}]: handler failed`);
console.debug(exc);
lastError = exc;
}
}
this.error = found ? undefined : (lastError ?? "Unspecified error").toString();
};
const scriptElement = document.createElement("script");
scriptElement.src = this.challenge.jsUrl;
scriptElement.async = true;
scriptElement.defer = true;
scriptElement.dataset.akCaptchaScript = "true";
scriptElement.onload = attachCaptcha;
document.head
.querySelectorAll("[data-ak-captcha-script=true]")
.forEach((el) => el.remove());
document.head.appendChild(scriptElement);
if (!this.challenge.interactive) {
document.body.appendChild(this.captchaDocumentContainer);
}
}
}
declare global {

View File

@ -59,7 +59,7 @@ const maxInstances =
process.env.MAX_INSTANCES !== undefined
? parseInt(process.env.MAX_INSTANCES, DEFAULT_MAX_INSTANCES)
: runHeadless
? 10
? 1
: 1;
export const config: WebdriverIO.Config = {

View File

@ -6864,7 +6864,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -7057,6 +7057,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -7128,7 +7128,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -7321,6 +7321,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -6782,7 +6782,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -6975,6 +6975,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -9042,8 +9042,8 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<target>Synchroniser le groupe</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> (&amp;quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&amp;quot;, de type <x id="2" equiv-text="${prompt.type}"/>)</target>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${p.name}"/> (&amp;quot;<x id="1" equiv-text="${p.fieldKey}"/>&amp;quot;, de type <x id="2" equiv-text="${p.type}"/>)</target>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -9300,6 +9300,26 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
<target>URLs de redirection/Origines</target>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
<target>Sources OIDC fédérées</target>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
<target>Fournisseurs OIDC fédérés</target>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
<target>Fournisseurs disponibles</target>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
<target>Fournisseurs sélectionnés</target>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
<target>Les JWTs signés par les fournisseurs sélectionnés peuvent être utilisés pour s'authentifier auprès de ce fournisseur.</target>
</trans-unit>
</body>
</file>

View File

@ -9163,7 +9163,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Device type <x id="0" equiv-text="${device.verboseName}"/> cannot be deleted</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="s1ac2653a6492b435">
<source><x id="0" equiv-text="${this.outpostHealth.version}"/>, should be <x id="1" equiv-text="${this.outpostHealth.versionShould}"/></source>
@ -9250,6 +9250,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8691,7 +8691,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -8884,6 +8884,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8539,7 +8539,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -8732,6 +8732,21 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8954,7 +8954,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -9147,6 +9147,21 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8990,7 +8990,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>Śŷńć Ĝŕōũƥ</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", ōƒ ţŷƥē <x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
@ -9187,4 +9187,19 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body></file></xliff>

View File

@ -9017,7 +9017,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -9210,6 +9210,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8995,7 +8995,7 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<target>Grubu Eşitle</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</target>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
@ -9240,6 +9240,21 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -5714,7 +5714,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -5908,6 +5908,21 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -9044,7 +9044,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<target>同步组</target>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
<target><x id="0" equiv-text="${prompt.name}"/>&amp;quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&amp;quot;,类型为 <x id="2" equiv-text="${prompt.type}"/></target>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
@ -9302,6 +9302,21 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
<target>重定向 URI/Origin</target>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -6823,7 +6823,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -7016,6 +7016,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -8652,7 +8652,7 @@ Bindings to groups/users are checked against the user of the event.</source>
<source>Sync Group</source>
</trans-unit>
<trans-unit id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
</trans-unit>
<trans-unit id="sa38c5a2731be3a46">
<source>authentik was unable to save this application:</source>
@ -8845,6 +8845,21 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="s43f899a86c6a3484">
<source>Redirect URIs/Origins</source>
</trans-unit>
<trans-unit id="sa52bf79fe1ccb13e">
<source>Federated OIDC Sources</source>
</trans-unit>
<trans-unit id="s555465bf6577505e">
<source>Federated OIDC Providers</source>
</trans-unit>
<trans-unit id="s066bfb12c9032dc2">
<source>Available Providers</source>
</trans-unit>
<trans-unit id="sa12111ca3f3e398e">
<source>Selected Providers</source>
</trans-unit>
<trans-unit id="s4f8a3f7792e6b940">
<source>JWTs signed by the selected providers can be used to authenticate to this provider.</source>
</trans-unit>
</body>
</file>

View File

@ -1,4 +1,4 @@
# Machine-to-machine authentication
## Machine-to-machine authentication
Client credentials can be used for machine-to-machine communication authentication. Clients can authenticate themselves using service-accounts; standard client_id + client_secret is not sufficient. This behavior is due to providers only being able to have a single secret at any given time.

View File

@ -0,0 +1,19 @@
---
title: Create an OAuth2 provider
---
To add a provider (and the application that uses the provider for authentication) use the Application Wizard, which creates both the new application and the required provider at the same time. For typical scenarios, authentik recommends that you use the Wizard to create both the application and the provider together. (Alternatively, use our legacy process: navigate to **Applications --> Providers**, and then click **Create**.)
1. Log into authentik as an admin, and navigate to **Applications --> Applications**.
2. Click **Create with Wizard**.
3. In the **New application** wizard, define the application details, and then click **Next**.
4. Select the **Provider Type** of **OAuth2/OIDC**, and then click **Next**.
5. On the **Configure OAuth2/OpenId Provider** page, provide the configuration settings and then click **Submit** to create and save both the application and the provider.
:::info
Optionally, configure the provider to have the `offline_access` scope mapping. Starting with authentik 2024.2, by default applications only receive an access token. To receive a refresh token, both applications and authentik must be configured to request the `offline_access` scope. Do this in the Scope mapping area on the **Configure OAuth2/OpenId Provider** page.
:::

View File

@ -0,0 +1,23 @@
---
title: GitHub compatibility
---
The OAuth2 provider also exposes a GitHub-compatible endpoint. This endpoint can be used by applications, which support authenticating against GitHub Enterprise, but not generic OpenID Connect.
To use any of the GitHub Compatibility scopes, you have to use the GitHub Compatibility Endpoints.
| Endpoint | URL |
| --------------- | --------------------------- |
| Authorization | `/login/oauth/authorize` |
| Token | `/login/oauth/access_token` |
| User Info | `/user` |
| User Teams Info | `/user/teams` |
To access the user's email address, a scope of `user:email` is required. To access their groups, `read:org` is required. Because these scopes are handled by a different endpoint, they are not customisable as a Scope Mapping.
## Special scopes for GitHub compatibility
- `user`: No-op, is accepted for compatibility but does not give access to any resources
- `read:user`: Same as above
- `user:email`: Allows read-only access to `/user`, including email address
- `read:org`: Allows read-only access to `/user/teams`, listing all the user's groups as teams.

View File

@ -1,8 +1,124 @@
---
title: OAuth2 Provider
title: OAuth 2.0 provider
---
This provider supports both generic OAuth2 as well as OpenID Connect
In authentik, you can [create](./create-oauth2-provider.md) an [OAuth 2.0](https://oauth.net/2/) provider that authentik uses to authenticate the user to the associated application. This provider supports both generic OAuth2 as well as OpenID Connect (OIDC).
## authentik and OAuth 2.0
It's important to understand how authentik works with and supports the OAuth 2.0 protocol, so before taking a [closer look at OAuth 2.0 protocol](#about-oauth-20-and-oidc) itself, let's cover a bit about authentik.
authentik can act either as the OP, (OpenID Provider, with authentik as the IdP), or as the RP (Relying Party, or the application that uses OAuth 2.0 to authenticate). If you want to configure authentik as an OP, then you create a provider, then use the OAuth 2.0 provider. If you want authentik to serve as the RP, then configure a [source](../../../users-sources/sources/index.md). Of course, authentik can serve as both the RP and OP, if you want to use the authentik OAuth provider and also use sources.
All standard OAuth 2.0 flows (authorization code, client_credentials, implicit, hybrid, device code) and grant types are supported in authentik, and we follow the [OIDC spec](https://openid.net/specs/openid-connect-core-1_0.html). OAuth 2.0 in authentik supports OAuth, PKCE, [Github compatibility](./github-compatibility.md) and the RP receives data from our scope mapping system.
The authentik OAuth 2.0 provider comes with all the standard functionality and features of OAuth 2.0, including the OAuth 2.0 security principles such as no cleartext storage of credentials, configurable encryption, configurable short expiration times, and the configuration of automatic rotation of refresh tokens. In short, our OAuth 2.0 protocol support provides full coverage.
## About OAuth 2.0 and OIDC
OAuth 2.0 is an authorization protocol that allows an application (the RP) to delegate authorization to an OP. OIDC is an authentication protocol built on top of OAuth2, which provides identity credentials and other data on top of OAuth2.
**OAuth 2.0** typically requires two requests (unlike the previous "three-legged" OAuth 1.0). The two "legs", or requests, for OAuth 2.0 are:
1. An authorization request is prepared by the RP and contains parameters for its implementation of OAuth and which data it requires, and then the User's browser is redirected to that URL.
2. The RP sends a request to authentik in the background to exchange the access code for an access token (and optionally a refresh token).
In detail, with OAuth2 when a user accesses the application (the RP) via their browser, the RP then prepares a URL with parameters for the OpenID Provider (OP), which the users's browser is redirected to. The OP authenticates the user and generates an authorization code. The OP then redirects the client (the user's browser) back to the RP, along with that authorization code. In the background, the RP then sends that same authorization code in a request authenticated by the `client_id` and `client_secret` to the OP. Finally, the OP responds by sending an Access Token saying this user has been authorised (the RP is recommended to validate this token using cryptography) and optionally a Refresh Token.
The image below shows a typical authorization code flow.
```mermaid
sequenceDiagram
participant user as User
participant rp as RP (Relying Party)
participant op as OP (OpenID Provider)
user->>rp: User accesses application
rp->>user: RP prepares authorization request & redirects user to OP
user->>op: User authentication & authorization occurs
op->>rp: Redirect back to the RP with an authorization code
rect rgb(255, 255, 191)
rp->>op: Exchange authorization code
op->>rp: RP receives Access token (optionally Refresh Token)
end
rp->>user: User is logged in
```
### Additional configuration options with Redirect URIs
When using an OAuth 2.0 provider in authentik, the OP must validate the provided redirect URI by the RP. An authentik admin can configure a list in the **Redirect URI** field on the Provider.
When you create a new OAuth 2.0 provider and app in authentik and you leave the **Redirect URI** field empty, then the first time a user opens that app, authentik uses that URL as the saved redirect URL.
For advanced use cases, an authentik admin can use regular expressions (regex) instead of a redirect URL. For example, if you want to list 10 diff applications, instead of listing all ten you can create an expression with wildcards. Be aware, when using regex, that authetnik uses a dot as a separator in the URL, but in regex a dot means "one of any character", a wildcard. So you should escape the dot to prevent its interpration as a wildcard.
## OAuth 2.0 flows and grant types
There are three general flows of OAuth 2.0:
1. Web-based application authorization (Authorization code, Implicit, Refresh token)
2. Client credentials (Machine-to-machine)
3. Device code
Additionally, the [Refresh token](#refresh-token-grant) (grant type) is optionally used with any of the above flows, as well as the client credentials and device code flows.
### 1: Web-based application authorization
The flows and grant types used in this case are those used for a typical authorization process, with a user and an application:
- _Authorization code_ grant type
- _Implicit_ grant type (legacy)
- _Hybrid_ grant type
#### Authorization code
The authorization code is for environments with both a Client and a application server, where the back and forth happens between the client and an app server (the logic lives on app server). The RP needs to authorise itself to the OP. Clint ID (public, identifies which app is talking to it) and client secret (the password) that the RP uses to authenticate.
If you configure authentik to use "Offline access" then during the initial auth the OP sends two tokens, an access token (short-lived, hours, can be customised) and a refresh token (typically longer validity, days or infinite). The RP (the app) saves both tokens. When the access token is about to expire, the RP sends the saved refresh token back to the OP, and requests a new access token. When the refresh token itself is about to expire, the RP can also ask for a new refresh token. This can all happen without user interaction if you configured the offline access.
:::info
Starting with authentik 2024.2, applications only receive an access token. To receive a refresh token, both applications and authentik must be configured to request the `offline_access` scope. In authentik this can be done by selecting the `offline_access` Scope mapping in the provider settings.
:::
The authorization code grant type is used to convert an authorization code to an access token (and optionally a refresh token). The authorization code is retrieved through the authentik [Authorization flow](../../flows-stages/flow/index.md), can only be used once, and expires quickly.
#### Implicit
:::info
The OAuth 2.0 [Security Best Current Practice document](https://tools.ietf.org/html/draft-ietf-oauth-security-topics) recommends against using the Implicit flow entirely, and OAuth 2.0 for Browser-Based Apps describes the technique of using the authorization code flow with PKCE instead. ([source](https://oauth.net/2/grant-types/implicit/))
:::
This flow is for more modern single page-applications, or ones you download, that are all client-side (all JS, no backend logic, etc) and have no server to make tokens. Because the secret cannot be stored on the client machine, the implicit flow is required in these architectures. With the implicit flow, the flow skips the second part of the two requests seen in the authorization flow; after the initial author request, the implicit flow receives a token, and then with cryptocracy and with PKCE, it can validate that it is the correct client, and that is safe to send a token. The RP (still called that with this implicit flow) can use cryptography to validate the token.
#### Hybrid
The Hybrid Flow is an OpenID Connect flow that incorporates traits of both the Implicit flow and the Authorization Code flow. It provides an application instant access to an ID token while ensuring secure and safe retrieval of access tokens and refresh tokens. This can be useful in situations where the application needs to quickly access information about the user, while in the background doing further processing to get additional tokens before gaining access to additional resources.
### 2. Client credentials
The client credentials flow and grant types are typically implemented for server-to-server scenarios, when code in a web application invokes a web API.
For more information, see [Machine-to-machine authentication](./client_credentials.md).
### 3. Device code
The device code flow is used in situations where there is no browser and limited options for text or data input from a client ("input-constrained devices"). For example, using a subscription TV program on a television, where you use a website on your mobile device to input a code displayed on the TV, authenticate, and then you are logged in to the TV.
For more information, see [Device code flow](./device_code.md).
#### Refresh token grant
Refresh tokens can be used as long-lived tokens to access user data, and further renew the refresh token down the road.
:::info
Starting with authentik 2024.2, the refresh token grant type requires the `offline_access` scope.
:::
## Scope mappings
Scopes can be configured using scope mappings, a type of [property mapping](../property-mappings/index.md#scope-mappings).
@ -16,43 +132,6 @@ Scopes can be configured using scope mappings, a type of [property mapping](../p
| JWKS | `/application/o/<application slug>/jwks/` |
| OpenID Configuration | `/application/o/<application slug>/.well-known/openid-configuration` |
## GitHub Compatibility
This provider also exposes a GitHub-compatible endpoint. This endpoint can be used by applications, which support authenticating against GitHub Enterprise, but not generic OpenID Connect.
To use any of the GitHub Compatibility scopes, you have to use the GitHub Compatibility Endpoints.
| Endpoint | URL |
| --------------- | --------------------------- |
| Authorization | `/login/oauth/authorize` |
| Token | `/login/oauth/access_token` |
| User Info | `/user` |
| User Teams Info | `/user/teams` |
To access the user's email address, a scope of `user:email` is required. To access their groups, `read:org` is required. Because these scopes are handled by a different endpoint, they are not customisable as a Scope Mapping.
## Grant types
### `authorization_code`:
This grant is used to convert an authorization code to an access token (and optionally refresh token). The authorization code is retrieved through the Authorization flow, and can only be used once, and expires quickly.
:::info
Starting with authentik 2024.2, applications only receive an access token. To receive a refresh token, both applications and authentik must be configured to request the `offline_access` scope. In authentik this can be done by selecting the `offline_access` Scope mapping in the provider settings.
:::
### `refresh_token`:
Refresh tokens can be used as long-lived tokens to access user data, and further renew the refresh token down the road.
:::info
Starting with authentik 2024.2, this grant requires the `offline_access` scope.
:::
### `client_credentials`:
See [Machine-to-machine authentication](./client_credentials.md)
## Scope authorization
By default, every user that has access to an application can request any of the configured scopes. Starting with authentik 2022.4, you can do additional checks for the scope in an expression policy (bound to the application):
@ -66,14 +145,14 @@ return True
## Special scopes
#### GitHub compatibility
### GitHub compatibility
- `user`: No-op, is accepted for compatibility but does not give access to any resources
- `read:user`: Same as above
- `user:email`: Allows read-only access to `/user`, including email address
- `read:org`: Allows read-only access to `/user/teams`, listing all the user's groups as teams.
#### authentik
### authentik
- `goauthentik.io/api`: This scope grants the refresh token access to the authentik API on behalf of the user

View File

@ -1,7 +1,7 @@
Create a middleware:
```yaml
apiVersion: traefik.containo.us/v1alpha1
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: authentik
@ -24,6 +24,10 @@ spec:
- X-authentik-meta-version
```
:::info
Traefik changed the apiVersion of the middleware CRD in version 3.0, for older versions please subsititue "apiVersion: traefik.containo.us/v1alpha1"
:::
Add the following settings to your IngressRoute
By default traefik does not allow cross-namespace references for middlewares:

View File

@ -20,10 +20,6 @@ You can also use a native install, if you prefer.
If you use locally installed databases, the PostgreSQL credentials given to authentik should have permissions for `CREATE DATABASE` and `DROP DATABASE`, because authentik creates a temporary database for tests.
:::
:::warning
If you are running a development environment and you have read replicas configured in your `local.env.yaml` file, you might encounter problems logging back in. This is because dev environments do not have read replica databases, but the `local.env.yaml` file is configured to call them. If you cannot log in, edit your local `local.env.yaml` file to remove the `postgresql.read_replicas` entry.
:::
## Backend Setup
:::info

View File

@ -101,7 +101,15 @@ The following events occur when a license expires or the internal/external user
- After another 2 weeks, users get a warning banner
- After another 2 weeks, the authentik Enterprise instance becomes read-only
- After another 2 weeks, the authentik Enterprise instance becomes "read-only"
When an authentik instance is in read-only mode, the following actions are still possible:
- Users can authenticate and authorize applications
- Licenses can be modified
- Users can be modified/deleted <span class="badge badge--version">authentik 2024.10.5+</span>
After the violation is corrected (either the user count returns to be within the limits of the license or the license is renewed), authentik will return to the standard read-write mode and the notification will disappear.
### About users and licenses

View File

@ -99,10 +99,6 @@ The same PostgreSQL settings as described above are used for each read replica.
Note that `USE_PGBOUNCER` and `USE_PGPOOL` are inherited from the main database configuration and are _not_ overridable on read replicas.
:::warning
If you are running a [development environment](../../developer-docs/setup/full-dev-environment.md) be aware that read replicas are not supported.
:::
## Redis Settings
- `AUTHENTIK_REDIS__HOST`: Redis server host when not using configuration URL

View File

@ -24,7 +24,7 @@ Parameters:
Description: authentik server memory in MiB
Type: Number
AuthentikVersion:
Default: 2024.10.4
Default: 2024.10.5
Description: authentik Docker image tag
Type: String
AuthentikWorkerCPU:

View File

@ -179,7 +179,16 @@ helm upgrade authentik authentik/authentik -f values.yaml --version ^2024.10
- providers/oauth2: fix migration dependencies (cherry-pick #12123) (#12132)
- providers/oauth2: fix redirect uri input (cherry-pick #12122) (#12127)
- providers/proxy: fix redirect_uri (cherry-pick #12121) (#12125)
- web: bump API Client version (cherry-pick #12129) (#12130)
## Fixed in 2024.10.5
- enterprise: allow deletion/modification of users when in read-only mode (#12289)
- enterprise/stages/authenticator_endpoint_gdtc: don't set frame options globally (cherry-pick #12311) (#12315)
- flows: better test stage's challenge responses (cherry-pick #12316) (#12317)
- root: fix database ssl options not set correctly (cherry-pick #12180) (#12183)
- stages/identification: fix invalid challenge warning when no captcha stage is set (cherry-pick #12312) (#12314)
- web: backport fix impersonate api (#12184)
- web/flows: resize captcha iframes (cherry-pick #12260) (#12304)
## API Changes

View File

@ -0,0 +1,63 @@
# 2024-11 Cobalt pentest
We are committed to engaging in regular pentesting and security audits of authentik. Defining and adhering to a cadence of external testing ensures a stronger probability that our code base, our features, and our architecture is as secure and non-exploitable as possible.
In August-September of 2024, we had a pentest conducted by [Cobalt](https://www.cobalt.io). This document covers the findings of the audit, how we addressed the noted issues, and the subsequent [re-testing](https://github.com/goauthentik/website/src/resources/final_fullReport_authentik-cobalt-test-instance-august-2024-pt26135.pdf) by Cobalt to confirm that all issues were resolved.
Cobalt described their process for testing:
> This pentest was a manual assessment of the security of the applications functionality, business logic, and vulnerabilities, such as those cataloged in the Open Web Application Security Project OWASP) Top 10. The assessment also included a review of security controls and requirements listed in the OWASP Application Security Verification Standard (ASVS).
## Summary of findings
Overall, we are pleased with the report's findings and grateful for the opportunity to improve in every area we can.
> Cobalt reported "The pentesters found that the Authentik Security team implemented robust and up-to-date security practices throughout the application."
In total, there were 5 low-level and one info-level vulnerabilities reported. By early November 2024, all 6 vulnerabilities were addressed and released in the [2024.10.4 patch release](https://docs.goauthentik.io/docs/releases/2024.10#fixed-in-2024103).
## Responses to specific findings
From the audit, this is the complete list of findings, with information about how we addressed each.
### HTML Injection
**Issue**: A vulnerability existed through user-supplied names in our Flow diagrams, where the application did not properly sanitize or escape HTML input when parsing user-entered names. As a result, an attacker could inject arbitrary HTML or JavaScript code into the application, potentially leading to manipulation of the web page or execution of malicious scripts in the context of the user's session. (This action could only be performed by an authenticated admin user, and thus had little practical value as an attack vector.)
**Fix**: We added strict [DOMpurify](https://github.com/cure53/DOMPurify) configurations for any user-defined names in our diagrams. For details, refer to [Pull Request #11783](https://github.com/goauthentik/authentik/pull/11783).
### SVG images for icons possible XSS vulnerability
**Issue**: The pentesters discovered that the application was susceptible to insecure file upload and stored Cross-Site Scripting (XSS) vulnerabilities by uploading crafted SVG files that were used as application icons. (This action could only be performed by an authenticated admin user, and thus had little practical value as an attack vector.)
**Fix**: The fix was to add a CSP header to files that are stored in the `/media` directory of the installation. For details, refer to [Pull Request #12092](https://github.com/goauthentik/authentik/pull/12092).
### Vulnerability through footer links on website
**Issue**: It was found that the application was vulnerable to stored XSS through footer links. The footer section of the application accepted and displayed user-provided links without proper sanitization. This could allow an attacker to inject malicious scripts into these links, which would then stored and executed when other users access the footer links, leading to potential script execution in the context of the victim's session. (This action could only be performed by an authenticated admin user, and thus had little practical value as an attack vector.)
**Fix**: Again, as with the diagram issue above, we added strict [DOMpurify](https://github.com/cure53/DOMPurify) configurations. For more details, refer to [Pull Request #11773](https://github.com/goauthentik/authentik/pull/11773).
### Password policy weakness
**Issue**: It was determined that the password policy in place on the testing environment was weak, allowing users to create passwords that lacked complexity and were easily guessable. This made the application more susceptible to brute-force and dictionary attacks.
**Fix**: This was not a vulnerability in authentik, but rather a poor configuration of our provided test environment. Rather than simply improve our test instances configuration once, to make this issue easier to avoid for all our users and customers, we added a strong default password policy that applies to all new instances. (As always, admins can still configure their own custom policies.) For more details, refer to [Pull Request #11793](https://github.com/goauthentik/authentik/pull/11793).
### Lack of a CSP header
The absence of Content Security Policy (CSP) headers means that the application may lack a mechanism to restrict sources of content and scripts, which can potentially expose it to XSS attacks and other forms of content injection.
**Fix**: We added CSP headers to control the sources of content and scripts that the application can load for our provided test instance. Again, this is not a direct vulnerability in authentik itself. Given the variety of architectures in which authentik is deployed, adding our own CSP headers would be more likely to break functionality than to provide improved security.
### API endpoints strengthened
**Issue**: Finally, the only informational level finding was the potential for the unauthenticated download of private key and certificate values via a direct URL. (Guessing the URL required the knowledge of the UUID of an object.) We had already fixed this issue in 2024.8.0, but the instance tested against was the immediately preceding version.
**Fix**: For more details, refer to [CVE-2024-42490](https://docs.goauthentik.io/docs/security/cves/CVE-2024-42490).
## Retest results
The subsequent retest conducted by Cobalt deemed all issues resolved. See page 17 of the [report](https://github.com/goauthentik/website/src/resources/final_fullReport_authentik-cobalt-test-instance-august-2024-pt26135.pdf) for the mitigation status ("fixed") for each of the issues discovered in September.
We are pleased to share this pentest and the final results of the retest. We encourage an open and ongoing communication with our users and community. For more information abut our security stance, read our [Security Policy](https://docs.goauthentik.io/docs/security/policy), [Hardening authentik](https://docs.goauthentik.io/docs/security/security-hardening), and our other [security-related documentation](https://docs.goauthentik.io/docs/security). If you have any questions or feedback you can reach us on [GitHub](https://github.com/goauthentik/authentik), [Discord](https://discord.com/channels/809154715984199690/809154716507963434), or via email to [hello@goauthentik.io](mailto:hello@goauthentik.io).

View File

@ -0,0 +1,86 @@
---
title: Integrate with EspoCRM
sidebar_label: EspoCRM
---
# EspoCRM
<span class="badge badge--secondary">Support level: Community</span>
## What is EspoCRM?
> EspoCRM is a CRM (customer relationship management) web application that allows users to store, visualize, and analyze their company's business-related relationships such as opportunities, people, businesses, and projects.
>
> -- https://www.espocrm.com/
:::warning
This guide does _not_ cover Team Mapping. Please refer to EspoCRM's [documentation](https://docs.espocrm.com/administration/oidc/#team-mapping).
:::
## Preparation
The following placeholders will be used:
- `crm.<your_company>` is the FQDN of the EspoCRM install.
- `authentik.<your_company>` is the FQDN of the authentik install.
- `_SLUG_` is the slug you choose upon application create in authentik.
## authentik configuration
1. Log into authentik as an admin, and navigate to **Applications** --> **Applications**.
2. Click **Create with Wizard**.
:::info
Alternatively, use our legacy process and click **Create**. The legacy process requires that the application and its configuration provider be configured separately.
:::
3. In the _New Application_ wizard, define the application details, and then define the provider details with the following parameters:
- **Provider Type**: `OAuth2/OIDC (Open Authorization/OpenID Connect)`
- **Authorization Flow**: `default-provider-authorization-explicit-consent (Authorize Application)`
- **Client Type**: `Confidential`
- **Redirect URIs/Origins**: `https://crm.<your_company>/oauth-callback.php`
- **Scopes**: OpenID, Email, Profile, Proxy outpost
- **Subject Mode**: `Based on the User's username` (**OR** your preferred method; you can use the same username in authentik and EspoCRM)
- **Signing Key**: Select any available key
Note the `Client ID` and `Client Secret` values.
## EspoCRM configuration
### Access the OIDC auth
1. Login to your admin user at `crm.<your_company>`.
2. In EspoCRM at Administration > Authentication, select the OIDC method. Below, on the same form, a OIDC panel will appear.
### Configure the OIDC auth
1. Configure the following variables:
- **Client ID**: enter the `Client ID` from authentik
- **Client Secret**: enter the `Client Secret` from authentik
- **Authorization Redirect URI**: `https://crm.<your_company>/oauth-callback.php`
- **Fallback Login**: _Select this option if you want EspoCRM's login as fallback._
- **Allow OIDC login for admin users**: _Select this option if you want EspoCRM's admin users to be able to log in via OIDC._
_The following values are listed as slugs for clarity. An example for the first variable is included._
You can also view the full URLs on the provider's page in authentik's Admin interface.
- **Authorization Endpoint**: `/application/o/authorize/`
- (e.g. `https://crm.<your_company>/application/o/authorize/`)
- **Token Endpoint**: `/application/o/token/`
- **JSON Web Key Set Endpoint**: `/application/o/_SLUG_/jwks/`
- **Logout URL**: `application/o/_SLUG_/end-session/`
### Confirm the configuration
1. Select the `Save` option.
2. Access your EspoCRM instance (e.g. `crm.<your_company>`) in a private browser, and select `Sign In.`
- You will be presented with your authentik log-in screen.
- Enter your authentik credentials to proceed to EspoCRM!

View File

@ -28,12 +28,10 @@ The following placeholders will be used:
2. Create a new **OAuth2 / OpenID Provider**.
3. Ensure the **Client Type** is set to `Public`.
4. Note the generated **Client ID** and **Client Secret**.
5. In the provider settings, add the following redirect URLs under **Redirect URIs/Origins (RegEx)**:
```
https://netbird.company
https://netbird.company*
http://localhost:53000
```
5. In the provider settings, add the following redirect URLs under **Redirect URIs/Origins**:
- Strict; `https://netbird.company`
- Regex; `https://netbird.company/.*`
- Strict; `http://localhost:53000`
6. Under **Signing Key**, select an available key. By default, the authentik self-signed certificate is available.
7. Under **Advanced Protocol Settings**, set the **Access Code Validity** to `minutes=10` and set the **Subject Mode** to `Based on the User's ID`.
8. Click **Finish** to save the provider configuration.

View File

@ -0,0 +1,48 @@
---
title: Integrate with The Lounge
sidebar_label: The Lounge
---
# The Lounge
<span class="badge badge--secondary">Support level: Community</span>
## What is The Lounge
> The Lounge is a modern, web-based IRC (Internet Relay Chat) client that allows users to stay connected to IRC servers even when offline.
>
> -- https://thelounge.chat/
:::note
This guide assumes you already deployed an LDAP Provider, if not check [here](https://docs.goauthentik.io/docs/add-secure-apps/providers/ldap/generic_setup).
If you made any changes, e.g. using a different name for the user, make sure to apply them here as well.
:::
## Preparation
The following placeholders will be used:
- `authentik.company` is the FQDN of the authentik install.
- `dc=company,dc=com` the Base DN of the LDAP outpost. If you followed the LDAP provider guide this is: `dc=goauthentik,dc=io`
- `ldap_bind_user` the username of the desired LDAP Bind User. If you followed the LDAP provider guide this is: `ldapservice`
## LDAP Configuration
### authentik Configuration
Follow the [instructions](https://docs.goauthentik.io/docs/add-secure-apps/outposts/#create-and-configure-an-outpost) to create an LDAP outpost and configure access via the outpost. No additional authentik configuration needs to be configured.
### The Lounge configuration
In the `config.js` file find the `ldap` section and make the following changes:
1. Set `enable` to `true`
2. Set `url` to `ldap://authentik.company`
3. Set `primaryKey` to `cn`
4. In the `searchDN` section make the following changes:
1. Set `rootDN` to `cn=ldap_bind_user,ou=users,dc=company,dc=com`
2. Set `rootPassword` to the password you have given to the `ldap_bind_user`
3. Set `filter` to `(&(objectClass=user)`
1. Alternatively, if you want to restrict access by group, you can set it to: `(&(objectClass=user)(memberOf=cn=group_name,ou=groups,dc=ldap,dc=company,dc=com))`
4. Set `base` to `dc=ldap,dc=company,dc=com`
5. Finally, save the `config.js` file and restart The Lounge. You should be able to log in via LDAP now, as long as a user with the same name exists.

View File

@ -18,8 +18,8 @@
"@mdx-js/react": "^3.1.0",
"clsx": "^2.1.1",
"disqus-react": "^1.1.5",
"docusaurus-plugin-openapi-docs": "^4.2.0",
"docusaurus-theme-openapi-docs": "^4.2.0",
"docusaurus-plugin-openapi-docs": "^4.3.0",
"docusaurus-theme-openapi-docs": "^4.3.0",
"postcss": "^8.4.49",
"prism-react-renderer": "^2.4.0",
"react": "^18.3.1",
@ -34,10 +34,10 @@
"@docusaurus/module-type-aliases": "^3.3.2",
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/types": "^3.3.2",
"@types/react": "^18.3.12",
"aws-cdk": "^2.171.1",
"@types/react": "^18.3.13",
"aws-cdk": "^2.172.0",
"cross-env": "^7.0.3",
"prettier": "3.4.1",
"prettier": "3.4.2",
"typescript": "~5.7.2",
"wireit": "^0.14.9"
},
@ -5370,9 +5370,9 @@
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
},
"node_modules/@types/react": {
"version": "18.3.12",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
"version": "18.3.13",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.13.tgz",
"integrity": "sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg==",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@ -6027,9 +6027,9 @@
}
},
"node_modules/asn1.js/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/assert": {
@ -6121,9 +6121,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.171.1",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.171.1.tgz",
"integrity": "sha512-IWENyT4F5UcLr1szLsbipUdjIHn8FD3d/RvaIvhs2+qCamkfEV5mqv/ChMvRJ8H2jebhIZ2iz74or9O5Ismp+Q==",
"version": "2.172.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.172.0.tgz",
"integrity": "sha512-kacztcAl12F6zlBqKCuzCZmj4vrbMhzgDAxBB4T7fXR2amQyuu6W0nWcGWWvASXeBJcw2DJ6ulpfV4wuc9dksw==",
"dev": true,
"bin": {
"cdk": "bin/cdk"
@ -6441,13 +6441,17 @@
}
},
"node_modules/browserify-rsa": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
"integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz",
"integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==",
"license": "MIT",
"dependencies": {
"bn.js": "^5.0.0",
"randombytes": "^2.0.1"
"bn.js": "^5.2.1",
"randombytes": "^2.1.0",
"safe-buffer": "^5.2.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/browserify-sign": {
@ -6911,13 +6915,16 @@
}
},
"node_modules/cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
"integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
"inherits": "^2.0.4",
"safe-buffer": "^5.2.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/classnames": {
@ -7493,9 +7500,9 @@
}
},
"node_modules/create-ecdh/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/create-hash": {
@ -7558,25 +7565,29 @@
}
},
"node_modules/crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
"integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz",
"integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==",
"license": "MIT",
"dependencies": {
"browserify-cipher": "^1.0.0",
"browserify-sign": "^4.0.0",
"create-ecdh": "^4.0.0",
"create-hash": "^1.1.0",
"create-hmac": "^1.1.0",
"diffie-hellman": "^5.0.0",
"inherits": "^2.0.1",
"pbkdf2": "^3.0.3",
"public-encrypt": "^4.0.0",
"randombytes": "^2.0.0",
"randomfill": "^1.0.3"
"browserify-cipher": "^1.0.1",
"browserify-sign": "^4.2.3",
"create-ecdh": "^4.0.4",
"create-hash": "^1.2.0",
"create-hmac": "^1.1.7",
"diffie-hellman": "^5.0.3",
"hash-base": "~3.0.4",
"inherits": "^2.0.4",
"pbkdf2": "^3.1.2",
"public-encrypt": "^4.0.3",
"randombytes": "^2.1.0",
"randomfill": "^1.0.4"
},
"engines": {
"node": "*"
"node": ">= 0.10"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/crypto-js": {
@ -8793,9 +8804,9 @@
}
},
"node_modules/diffie-hellman/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/dir-glob": {
@ -8832,14 +8843,12 @@
}
},
"node_modules/docusaurus-plugin-openapi-docs": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-4.2.0.tgz",
"integrity": "sha512-6G8LtpQlb4nCTyDnjkRwxg/oq3WIZ05GCFQFfTqbdiI5D8d9Glya0vp6TF9InEyXtAkc8ozi1HP8It2zEUwNGw==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-4.3.0.tgz",
"integrity": "sha512-IOxJzLfH8vhGvzQSd9TI99w2naD705r838gaa1/oDlmcaajXYtKePkk7YwcqjLLKgJ/roQcvQAK2qNX/sijQvQ==",
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^11.5.4",
"@docusaurus/plugin-content-docs": "^3.5.0",
"@docusaurus/utils": "^3.5.0",
"@docusaurus/utils-validation": "^3.5.0",
"@redocly/openapi-core": "^1.10.5",
"allof-merge": "^0.6.6",
"chalk": "^4.1.2",
@ -8859,6 +8868,9 @@
"node": ">=14"
},
"peerDependencies": {
"@docusaurus/plugin-content-docs": "^3.5.0",
"@docusaurus/utils": "^3.5.0",
"@docusaurus/utils-validation": "^3.5.0",
"react": "^16.8.4 || ^17.0.0 || ^18.0.0"
}
},
@ -8949,6 +8961,7 @@
"resolved": "https://registry.npmjs.org/docusaurus-plugin-sass/-/docusaurus-plugin-sass-0.2.5.tgz",
"integrity": "sha512-Z+D0fLFUKcFpM+bqSUmqKIU+vO+YF1xoEQh5hoFreg2eMf722+siwXDD+sqtwU8E4MvVpuvsQfaHwODNlxJAEg==",
"license": "MIT",
"peer": true,
"dependencies": {
"sass-loader": "^10.1.1"
},
@ -8962,6 +8975,7 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -8978,6 +8992,7 @@
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
@ -8986,13 +9001,15 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/docusaurus-plugin-sass/node_modules/sass-loader": {
"version": "10.5.2",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz",
"integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"klona": "^2.0.4",
"loader-utils": "^2.0.0",
@ -9030,6 +9047,7 @@
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@ -9044,22 +9062,20 @@
}
},
"node_modules/docusaurus-theme-openapi-docs": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-4.2.0.tgz",
"integrity": "sha512-TTvrF3mOt9VjDJHu5Ga7jPZyPxhu6//FKTLXJrcGrQ/OOMnojCZZX0KX7zSUL96yF8VlRjwSeM9D90m+dZ9gHw==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-4.3.0.tgz",
"integrity": "sha512-tfuvnPiTy+HvKYHR+s0651W5rotHlAZtNVfI/6rl3OYmIw9v2abO9bcUt86JGvmLKYlSKNCtJIotLTY801j/TA==",
"license": "MIT",
"dependencies": {
"@docusaurus/theme-common": "^3.5.0",
"@hookform/error-message": "^2.0.1",
"@reduxjs/toolkit": "^1.7.1",
"allof-merge": "^0.6.6",
"clsx": "^1.1.1",
"copy-text-to-clipboard": "^3.1.0",
"crypto-js": "^4.1.1",
"docusaurus-plugin-openapi-docs": "^4.2.0",
"docusaurus-plugin-sass": "^0.2.3",
"file-saver": "^2.0.5",
"lodash": "^4.17.20",
"node-polyfill-webpack-plugin": "^2.0.1",
"node-polyfill-webpack-plugin": "^3.0.0",
"postman-code-generators": "^1.10.1",
"postman-collection": "^4.4.0",
"prism-react-renderer": "^2.3.0",
@ -9073,6 +9089,7 @@
"remark-gfm": "3.0.1",
"sass": "^1.80.4",
"sass-loader": "^16.0.2",
"unist-util-visit": "^5.0.0",
"webpack": "^5.61.0",
"xml-formatter": "^2.6.1"
},
@ -9080,6 +9097,9 @@
"node": ">=14"
},
"peerDependencies": {
"@docusaurus/theme-common": "^3.5.0",
"docusaurus-plugin-openapi-docs": "^4.0.0",
"docusaurus-plugin-sass": "^0.2.3",
"react": "^16.8.4 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0"
}
@ -10188,9 +10208,10 @@
"integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="
},
"node_modules/elliptic": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz",
"integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==",
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
"integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
"license": "MIT",
"dependencies": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
@ -10202,9 +10223,9 @@
}
},
"node_modules/elliptic/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/emoji-regex": {
@ -10599,9 +10620,9 @@
"license": "BSD-3-Clause"
},
"node_modules/express": {
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@ -10622,7 +10643,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.10",
"path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@ -10637,6 +10658,10 @@
},
"engines": {
"node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/content-disposition": {
@ -10664,9 +10689,9 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/express/node_modules/path-to-regexp": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/express/node_modules/range-parser": {
"version": "1.2.1",
@ -10904,15 +10929,6 @@
"node": ">=8"
}
},
"node_modules/filter-obj": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.2.tgz",
"integrity": "sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
@ -11636,16 +11652,16 @@
}
},
"node_modules/hash-base": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
"integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==",
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz",
"integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
"inherits": "^2.0.4",
"safe-buffer": "^5.2.1"
},
"engines": {
"node": ">=4"
"node": ">= 0.10"
}
},
"node_modules/hash.js": {
@ -13062,6 +13078,7 @@
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz",
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 8"
}
@ -16083,9 +16100,9 @@
}
},
"node_modules/miller-rabin/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/mime": {
@ -16264,16 +16281,15 @@
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@ -16367,12 +16383,12 @@
}
},
"node_modules/node-polyfill-webpack-plugin": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz",
"integrity": "sha512-ZUMiCnZkP1LF0Th2caY6J/eKKoA0TefpoVa68m/LQU1I/mE8rGt4fNYGgNuCcK+aG8P8P43nbeJ2RqJMOL/Y1A==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-3.0.0.tgz",
"integrity": "sha512-QpG496dDBiaelQZu9wDcVvpLbtk7h9Ctz693RaUMZBgl8DUoFToO90ZTLKq57gP7rwKqYtGbMBXkcEgLSag2jQ==",
"license": "MIT",
"dependencies": {
"assert": "^2.0.0",
"assert": "^2.1.0",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"console-browserify": "^1.2.0",
@ -16380,40 +16396,30 @@
"crypto-browserify": "^3.12.0",
"domain-browser": "^4.22.0",
"events": "^3.3.0",
"filter-obj": "^2.0.2",
"https-browserify": "^1.0.0",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"punycode": "^2.1.1",
"punycode": "^2.3.0",
"querystring-es3": "^0.2.1",
"readable-stream": "^4.0.0",
"readable-stream": "^4.4.2",
"stream-browserify": "^3.0.0",
"stream-http": "^3.2.0",
"string_decoder": "^1.3.0",
"timers-browserify": "^2.0.12",
"tty-browserify": "^0.0.1",
"type-fest": "^2.14.0",
"url": "^0.11.0",
"util": "^0.12.4",
"type-fest": "^4.4.0",
"url": "^0.11.3",
"util": "^0.12.5",
"vm-browserify": "^1.1.2"
},
"engines": {
"node": ">=12"
"node": ">=14"
},
"peerDependencies": {
"webpack": ">=5"
}
},
"node_modules/node-polyfill-webpack-plugin/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/node-polyfill-webpack-plugin/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
@ -16430,6 +16436,18 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": {
"version": "4.30.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.0.tgz",
"integrity": "sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/node-readfiles": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz",
@ -18855,15 +18873,6 @@
"node": ">=10"
}
},
"node_modules/postman-code-generators/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/postman-code-generators/node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -18958,19 +18967,10 @@
"node": ">=10"
}
},
"node_modules/postman-url-encoder/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/prettier": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz",
"integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@ -19131,16 +19131,19 @@
}
},
"node_modules/public-encrypt/node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"version": "4.12.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
"integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
"license": "MIT"
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/pupa": {
"version": "3.1.0",
@ -22860,23 +22863,17 @@
"punycode": "^2.1.0"
}
},
"node_modules/uri-js/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/url": {
"version": "0.11.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
"integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==",
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz",
"integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==",
"license": "MIT",
"dependencies": {
"punycode": "^1.4.1",
"qs": "^6.11.2"
"qs": "^6.12.3"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/url-loader": {
@ -22976,6 +22973,12 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/url/node_modules/punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
"license": "MIT"
},
"node_modules/use-editable": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/use-editable/-/use-editable-2.3.3.tgz",
@ -23625,9 +23628,9 @@
}
},
"node_modules/which-typed-array": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
"integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
"version": "1.1.16",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz",
"integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==",
"license": "MIT",
"dependencies": {
"available-typed-arrays": "^1.0.7",

View File

@ -27,8 +27,8 @@
"@mdx-js/react": "^3.1.0",
"clsx": "^2.1.1",
"disqus-react": "^1.1.5",
"docusaurus-plugin-openapi-docs": "^4.2.0",
"docusaurus-theme-openapi-docs": "^4.2.0",
"docusaurus-plugin-openapi-docs": "^4.3.0",
"docusaurus-theme-openapi-docs": "^4.3.0",
"postcss": "^8.4.49",
"prism-react-renderer": "^2.4.0",
"react": "^18.3.1",
@ -55,10 +55,10 @@
"@docusaurus/module-type-aliases": "^3.3.2",
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/types": "^3.3.2",
"@types/react": "^18.3.12",
"aws-cdk": "^2.171.1",
"@types/react": "^18.3.13",
"aws-cdk": "^2.172.0",
"cross-env": "^7.0.3",
"prettier": "3.4.1",
"prettier": "3.4.2",
"typescript": "~5.7.2",
"wireit": "^0.14.9"
},

View File

@ -193,8 +193,10 @@ export default {
id: "add-secure-apps/providers/oauth2/index",
},
items: [
"add-secure-apps/providers/oauth2/create-oauth2-provider",
"add-secure-apps/providers/oauth2/client_credentials",
"add-secure-apps/providers/oauth2/device_code",
"add-secure-apps/providers/oauth2/github-compatibility",
],
},
"add-secure-apps/providers/saml/index",
@ -649,7 +651,10 @@ export default {
{
type: "category",
label: "Audits and Certificates",
items: ["security/audits-and-certs/2023-06-cure53"],
items: [
"security/audits-and-certs/2023-06-cure53",
"security/audits-and-certs/2024-11-cobalt",
],
},
{
type: "category",

View File

@ -19,6 +19,7 @@ module.exports = {
items: [
"services/bookstack/index",
"services/dokuwiki/index",
"services/espoCRM/index",
"services/hedgedoc/index",
"services/kimai/index",
"services/mastodon/index",
@ -33,6 +34,7 @@ module.exports = {
"services/roundcube/index",
"services/sharepoint-se/index",
"services/slack/index",
"services/thelounge/index",
"services/vikunja/index",
"services/wekan/index",
"services/wiki-js/index",