Compare commits

...

121 Commits

Author SHA1 Message Date
faf8bf591f providers/saml: configuration for default NameID Policy
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-18 11:39:28 +02:00
52115f9345 core, web: update translations (#15102)
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>
2025-06-18 11:15:00 +02:00
b476551f13 web: bump @sentry/browser from 9.29.0 to 9.30.0 in /web in the sentry group across 1 directory (#15104)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.29.0 to 9.30.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.29.0...9.30.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 11:14:24 +02:00
f9563c25cd core: bump github.com/jellydator/ttlcache/v3 from 3.3.0 to 3.4.0 (#15105)
Bumps [github.com/jellydator/ttlcache/v3](https://github.com/jellydator/ttlcache) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/jellydator/ttlcache/releases)
- [Commits](https://github.com/jellydator/ttlcache/compare/v3.3.0...v3.4.0)

---
updated-dependencies:
- dependency-name: github.com/jellydator/ttlcache/v3
  dependency-version: 3.4.0
  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>
2025-06-18 11:14:15 +02:00
0067e6e155 website: bump @types/lodash from 4.17.17 to 4.17.18 in /website (#15106)
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.17 to 4.17.18.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-version: 4.17.18
  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>
2025-06-18 11:14:08 +02:00
ce183929d4 website/integrations: zipline: add offline_access scope (#15099)
Closes https://github.com/goauthentik/authentik/issues/15098

Signed-off-by: Dominic R <dominic@sdko.org>
2025-06-17 16:15:07 -05:00
2fdf345271 website/docs: Add steps to troubleshoot /initial-setup/ (#15011)
* Add steps to troubleshoot /initial-setup/

* fix linting

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* add email part

* Apply suggestions from code review

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* change wording

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-06-17 08:40:14 -05:00
bbcf8418b4 core, web: update translations (#15084)
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>
2025-06-17 13:50:05 +02:00
dc57be46f4 website: bump the eslint group in /website with 3 updates (#15085)
Bumps the eslint group in /website with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/parser)

Updates `typescript-eslint` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:31:27 +02:00
d68b3ba516 website: bump @types/node from 24.0.1 to 24.0.3 in /website (#15086)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.0.1 to 24.0.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.3
  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>
2025-06-17 13:31:20 +02:00
a9c46cfcbd website: bump postcss from 8.5.5 to 8.5.6 in /website (#15087)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.5 to 8.5.6.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.5...8.5.6)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.6
  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>
2025-06-17 13:31:12 +02:00
c50353ebf6 core: bump webauthn from 2.5.2 to 2.6.0 (#15089)
Bumps [webauthn](https://github.com/duo-labs/py_webauthn) from 2.5.2 to 2.6.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.5.2...v2.6.0)

---
updated-dependencies:
- dependency-name: webauthn
  dependency-version: 2.6.0
  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>
2025-06-17 13:30:57 +02:00
db6be9e1b6 core: bump goauthentik.io/api/v3 from 3.2025061.2 to 3.2025062.1 (#15090)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025061.2 to 3.2025062.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.2025061.2...v3.2025062.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025062.1
  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>
2025-06-17 13:30:50 +02:00
a74892886d web: bump the eslint group across 2 directories with 3 updates (#15091)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 1 update in the /web directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `typescript-eslint` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/parser)

Updates `typescript-eslint` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.34.0 to 8.34.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/parser)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 13:30:42 +02:00
74cd4c2236 translate: Updates for file web/xliff/en.xlf in zh_CN (#15074)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-17 13:28:56 +02:00
ef3bd7e77b translate: Updates for file web/xliff/en.xlf in zh-Hans (#15075)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-17 13:28:42 +02:00
3f5ad2baa4 ci: fix post-release e2e builds failing (#15082)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-17 09:10:26 +02:00
24805f087b web: bump API Client version (#15079)
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>
2025-06-17 01:51:07 +02:00
9464b422a3 web/common: fix uiConfig not merged correctly (#15080)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-17 01:36:39 +02:00
da6d4ede51 root: backport version bump 2025.6.2 (#15078)
release: 2025.6.2
2025-06-17 00:21:39 +02:00
cecad5bfd3 website/integrations: add note to nextcloud OIDC config (#15073)
Add note to OIDC config
2025-06-16 16:47:16 +00:00
bc4b07d57b web/admin: remove all special cases of slug handling, replace with a "smart slug" component (#14983)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/components: Remove all special cases of slug handling, replace with a "smart slug" component

This commit removes all special handling for the `slug` attribute in our text. A variant of the text
input control that can handle formatting-as-slugs has replaced all the slugificiation code; simply
drop it onto a page and tell it the (must be unique) selector from which to get the data to be
slugified. It only looks up one tier of the DOM so be careful that both the text input and its slug
accessory occupy the same DOM context.

## Details

### The Component

Now that we know a (lot) more about Lit, this component has been slightly updated to meet our
current standards.

- web/src/components/ak-slug-input.ts

Changes made:

- The "listen for the source object" has been moved to the `firstUpdated`, so that it no longer has
  to wait for the end of a render.
 - The `dirtyFlag` handler now uses the `@input` syntax.
- Updated the slug formatter to permit trailing dashes.
- Uses the `@bound` decorator, eliminating the need to do binding in the constructor (and so
  eliminating the constructor completely).

### Component uses:

The following components were revised to use `ak-slug-input` instead of a plain text input with the
slug-handling added by our forms manager.

- applications/ApplicationForm.ts
- flows/FlowForm.ts
- sources/kerberos/KerberosSourceForm.ts
- sources/ldap/LDAPSourceForm.ts
- sources/oauth/OAuthSourceForm.ts
- sources/plex/PlexSourceForm.ts
- sources/saml/SAMLSourceForm.ts
- sources/scim/SCIMSourceForm.ts

### Remove the redundant special slug handling code

- web/src/elements/forms/Form.ts
- web/src/elements/forms/HorizontalFormElement.ts

### A special case among special cases

- web/src/admin/stages/invitation/InvitationForm.ts

This form is our one case where we have a slug input field with no corresponding text source. Adding
a simple event handler to validate the value whenever it changed and write back a "clean" slug was
the most straightforward solution. I added a help line; it seemed "surprising" to ask someone for a
name and not follow the same rules as "names" everywhere else in our UI without explanation.

* After writing the commit message, I realized some of the comments I made MUST be added to the component.

* The `source` attribute needed its own comment to indicate that a `query()` compatible selector is expected.

* Added public/private/protected/# indicators to all fields.  Trying to balance between getting it 'right' and leaving an opening for harmonizing style-sharing and state-sharing between (text / textarea), slug, password and (visible / hidden / secret).

* Removed the ids as requested; the default "look for this" matches the original behavior without requiring it be hard-coded and unchangable.
2025-06-16 09:04:00 -07:00
e85d2d0096 Web/cleanup/empty state better slot handling (#14289)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web: remove Lit syntax from always true attributes

## What

Replaces instances of `?loading=${true}` and `?loading="${true}"` with `loading`

## Why

The Lit syntax is completely unnecessary when the attribute's state is constant, and it's a few
(just a few) extra CPU cycles for Lit to process that.

More to the point, it annoys me.

## How

```
$ perl -pi.bak -e 's/\?loading=\$\{true\}/loading/' $(rg -l '\?loading=\$\{true\}')
$ find . -name '*.bak' -exec rm {} \;
$ perl -pi.bak -e 's/\?loading="\$\{true\}"/loading/' $(rg -l '\?loading="\$\{true\}"')
$ find . -name '*.bak' -exec rm {} \;
```

* Prettier had opinions

* web: move optional textual information out of attributes and into slots

## What

Replaces instances of:

```
<ak-empty-state header=${msg(...)}></ak-empty-state>
```

with

```
<ak-empty-state><span slot="header">${msg(...)}</span></ak-empty-state>
```

## Why

1. It's correct.
2. It lets us elide the decorations for any slots we aren't using.
3. It's preparation for moving to Patternfly 5
4. It annoyed me.

## How

Since we already have Patternfly Elements installed, we have access to the PFE-Core, which has the
unbelievable useful `SlotsController`.  Using it, I created a conditional render template that will
only put in the header, body, and primary slots if there is something in the lightDOM requesting
those slots.  The conditional template will still put the spinner in if the header is not provided
but the loading state is true.

I then had to edit all the places where this is used. For about 30 of them, this script sufficed:

```
perl -pi.bak -e 's/header="?(\$\{msg\([^)]+\)\})"?>/><span slot="header">\1<\/span>/' \
     $(rg -l `<ak-empty-state[^>]header')

```

The other six had to be done by hand.  I have tested a handful of the automatic ones, and all of the
ones that were edited manually.  I'm pleasantly surprised that the textual rules [are inherited by
the slots as expected](https://htmlwithsuperpowers.netlify.app/styling/inheritable.htm).
2025-06-16 08:17:11 -07:00
be1dd3103b website/docs: release notes for 2025.6.2 (#15065)
* website/docs: release notes for `2025.6.2`

* fixup! website/docs: release notes for `2025.6.2`
2025-06-16 17:01:56 +02:00
5dfde5e1d3 website/docs: remove commented out config options (#15064)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 16:35:23 +02:00
7cb1e6d81e website/docs: postgres troubleshooting: get PGPASSWORD from POSTGRES_PASSWORD_FILE (#15039) 2025-06-16 15:00:23 +02:00
d7c3129b1c core: bump goauthentik/fips-python from 3.13.4-slim-bookworm-fips to 3.13.5-slim-bookworm-fips (#15058)
core: bump goauthentik/fips-python

Bumps goauthentik/fips-python from 3.13.4-slim-bookworm-fips to 3.13.5-slim-bookworm-fips.

---
updated-dependencies:
- dependency-name: goauthentik/fips-python
  dependency-version: 3.13.5-slim-bookworm-fips
  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>
2025-06-16 12:28:39 +02:00
2a1d33021b website: bump the eslint group in /website with 2 updates (#15059)
Bumps the eslint group in /website with 2 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) and [eslint](https://github.com/eslint/eslint).


Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:28:21 +02:00
f273e49ae6 web: bump the wdio group across 1 directory with 3 updates (#14593)
Bumps the wdio group with 3 updates in the /web directory: [@wdio/browser-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-browser-runner), [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/browser-runner` from 9.4.1 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-browser-runner)

Updates `@wdio/cli` from 9.4.1 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-cli)

Updates `@wdio/spec-reporter` from 9.1.2 to 9.14.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v9.14.0/packages/wdio-spec-reporter)

---
updated-dependencies:
- dependency-name: "@wdio/browser-runner"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
- dependency-name: "@wdio/cli"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
- dependency-name: "@wdio/spec-reporter"
  dependency-version: 9.14.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:28:10 +02:00
cc31957900 web: bump @sentry/browser from 9.28.1 to 9.29.0 in /web in the sentry group across 1 directory (#15061)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.28.1 to 9.29.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.28.1...9.29.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:27:30 +02:00
b1ccdecc8e web: bump the eslint group across 2 directories with 2 updates (#15062)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [eslint](https://github.com/eslint/eslint).
Bumps the eslint group with 1 update in the /web directory: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

Updates `eslint` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.29.0)

Updates `@eslint/js` from 9.28.0 to 9.29.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.29.0/packages/js)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@eslint/js"
  dependency-version: 9.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:27:17 +02:00
34031003a4 core: bump axllent/mailpit from v1.26.0 to v1.26.1 in /tests/e2e (#15060)
Bumps axllent/mailpit from v1.26.0 to v1.26.1.

---
updated-dependencies:
- dependency-name: axllent/mailpit
  dependency-version: v1.26.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 12:27:01 +02:00
055e1d1025 core: bump pydantic from 2.11.5 to 2.11.7 (#15063)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.5 to 2.11.7.
- [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.11.5...v2.11.7)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.7
  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>
2025-06-16 12:26:52 +02:00
59a804273e providers/oauth2: bug fixes from conformance testing (#15056)
* check authorize request param earlier

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

* fix basic suite?

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

* another actual fix; don't return access_token when using response_type id_token

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

* only run basic+implicit for now, fix other tests

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

# Conflicts:
#	tests/openid_conformance/test_conformance.py

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 12:23:18 +02:00
bce70a1796 website/integrations: change nextcloud scope name to avoid confusion (#15050)
changed "profile" to "nextcloud"

Signed-off-by: Marlin <77961876+Keksmo@users.noreply.github.com>
2025-06-16 03:10:53 +02:00
e86c40a00c web/flow: cleanup WebAuthn helper functions (#14460)
* pass #1

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

* pass #2

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

* add polyfill

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 02:39:50 +02:00
20e07486ee web/elements: fix typo in localeComparator (#15054)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-16 01:37:38 +02:00
0cb7cf2c96 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#15049)
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>
2025-06-15 23:54:50 +02:00
07736a90b2 translate: Updates for file locale/en/LC_MESSAGES/django.po in es (#15047)
* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

* Translate locale/en/LC_MESSAGES/django.po in es

100% translated source file: 'locale/en/LC_MESSAGES/django.po'
on 'es'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-15 22:48:33 +02:00
ec28a86259 core, web: update translations (#15048)
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>
2025-06-15 22:48:21 +02:00
260800c60b blueprints: add section support for organisation (#15045)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-15 20:49:21 +02:00
ee4780394d core, web: update translations (#15043)
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>
2025-06-14 02:44:35 +02:00
23b746941f web/admin: adopt ak-hidden-text (#15042)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-14 02:22:14 +02:00
3c2ce40afd web/admin: Text and Textarea Fields that "hide" their contents until prompted (#15024)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/admin: Provide `hidden` text and textarea components

## Details

This commit provides two new elements (technically, since they're API-unaware), one for `<input
type="text">`, and one for `<textarea>`, that provide for the ability to create fields that are (or
can be) hidden. A new boolean attribute, `revealed`, shows the state of the component (the content
is therefore *not* revealed by default).

It also includes a third new element, `ak-visibility-toggle`, that creates a hide/show toggle with
all the right icons, styling, and eventing.  It's straightforward, and isolating it improved the
DX of everything that uses that feature by quite a bit.

Storybook stories (with autodoc documentation) have been provided for `ak-hidden-text-input`,
`ak-hidden-textarea-input`, and `ak-visibility-toggle`.

## Maintenance Notice

As a maintenance detail, the field `ak-private-text` has been renamed `ak-secret-text` to reflect
its usage, and the places where it was used have all been changed to reflect that update.

* web/component: embed styling (for now) to handle the lightDom/shadowDom/slot conflicts in HorizontalLightComponent and HorizontalFormElement

* Comments and Types. I really shouldn't have to catch this stuff with my eyeballs.

* fix typo

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-14 01:48:34 +02:00
2aceed285e providers/rac: fixes prompt data not being merged with connection_settings (#15037)
* Fixes line that pulls in prompt data

* fallback to old settings

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-13 18:54:20 +02:00
81e5fef667 website/docs: also hide the postgres pool_options setting (#15023) 2025-06-13 13:36:52 +00:00
7aa6593760 blueprints: sort schema items (#15022) 2025-06-13 13:34:49 +00:00
c40a17beb9 website: bump the build group in /website with 6 updates (#15027)
Bumps the build group in /website with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.12.0` | `1.12.1` |


Updates `@swc/core-darwin-arm64` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

Updates `@swc/core-linux-arm64-gnu` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

Updates `@swc/core-linux-x64-gnu` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

Updates `@swc/html-darwin-arm64` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

Updates `@swc/html-linux-arm64-gnu` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

Updates `@swc/html-linux-x64-gnu` from 1.12.0 to 1.12.1
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  dependency-version: 1.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 14:44:06 +02:00
335c9fbc10 core: bump astral-sh/uv from 0.7.12 to 0.7.13 (#15028)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.7.12 to 0.7.13.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.7.12...0.7.13)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.7.13
  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>
2025-06-13 14:44:04 +02:00
51b53caf61 core: bump twilio from 9.6.2 to 9.6.3 (#15029)
Bumps [twilio](https://github.com/twilio/twilio-python) from 9.6.2 to 9.6.3.
- [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.6.2...9.6.3)

---
updated-dependencies:
- dependency-name: twilio
  dependency-version: 9.6.3
  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>
2025-06-13 14:44:01 +02:00
989100a900 core: bump sentry-sdk from 2.29.1 to 2.30.0 (#15030)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.29.1 to 2.30.0.
- [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.29.1...2.30.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.30.0
  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>
2025-06-13 14:43:58 +02:00
8e1531d051 core: bump kubernetes from 32.0.1 to 33.1.0 (#15031)
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 32.0.1 to 33.1.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/v33.1.0/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/python/compare/v32.0.1...v33.1.0)

---
updated-dependencies:
- dependency-name: kubernetes
  dependency-version: 33.1.0
  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>
2025-06-13 14:43:22 +02:00
f6f37d6d92 core, web: update translations (#15026)
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>
2025-06-13 02:21:16 +02:00
5b6ca70f22 web: bump the sentry group across 1 directory with 2 updates (#15025)
Bumps the sentry group with 2 updates in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript) and @spotlightjs/spotlight.


Updates `@sentry/browser` from 9.28.0 to 9.28.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.28.0...9.28.1)

Updates `@spotlightjs/spotlight` from 2.13.3 to 3.0.0

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.28.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: sentry
- dependency-name: "@spotlightjs/spotlight"
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 02:09:40 +02:00
a74674c3d6 translate: Updates for file web/xliff/en.xlf in zh_CN (#15018)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-13 02:08:55 +02:00
f46984dec4 lifecycle/aws: bump aws-cdk from 2.1018.0 to 2.1018.1 in /lifecycle/aws (#15016)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1018.0 to 2.1018.1.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1018.1/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1018.1
  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>
2025-06-13 02:08:34 +02:00
c7963e4af7 website: bump postcss from 8.5.4 to 8.5.5 in /website (#15013)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.4 to 8.5.5.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.4...8.5.5)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.5
  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>
2025-06-13 02:08:21 +02:00
6e30b11974 website: bump @types/node from 24.0.0 to 24.0.1 in /website (#15014)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.0.0 to 24.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.1
  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>
2025-06-13 02:08:11 +02:00
13bd4069e4 core: fix transaction test case (#15021)
* move patched ct to root

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

* use our transaction test case as base

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

* fix...?

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

* well apparently that works

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-13 01:48:26 +02:00
3b913ac5ef translate: Updates for file web/xliff/en.xlf in zh-Hans (#15019)
Translate web/xliff/en.xlf in zh-Hans

100% translated source file: 'web/xliff/en.xlf'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2025-06-12 14:03:15 +02:00
fed094b317 website/docs: correct minor version in release notes (#15012)
Signed-off-by: Zeik0s <35345686+Zeik0s@users.noreply.github.com>
2025-06-11 22:13:53 +00:00
88d83c10a4 root: test label handling and error reporting in PytestTestRunner (#14000)
* root: test label handling and error reporting in PytestTestRunner

- Added a check for empty test labels and return a clear error instead of failing silently.
- Improved handling of dotted module paths + included support for single-module names.
- Replaced a RuntimeError with a printed error message when a test label can't be resolved.
- Wrapped the pytest.main call in a try/except to catch unexpected errors and print them nicely.

* Fix handling for test file without extension and lint

* oops

* little improvments too
2025-06-11 19:14:38 +02:00
5af2378738 outposts/ldap: Handle comma-separated attributes in LDAP search requests (#15000)
Closes https://github.com/goauthentik/authentik/issues/13539

When LDAP clients like Jira submit search requests with comma-separated attributes
(e.g., ["uid,cn,sn"] instead of ["uid", "cn", "sn"]), the LDAP outpost would return
an "Operations Error". Ths fix adds attribute normalization to properly handle
both formats by splitting comma separated attributes into individual entries.

Tests pass:
```
=== RUN   TestNormalizeAttributes
=== RUN   TestNormalizeAttributes/Empty_input
=== RUN   TestNormalizeAttributes/No_commas
=== RUN   TestNormalizeAttributes/Single_comma-separated_string
=== RUN   TestNormalizeAttributes/Mixed_input
=== RUN   TestNormalizeAttributes/With_spaces
=== RUN   TestNormalizeAttributes/Empty_parts
=== RUN   TestNormalizeAttributes/Single_element
=== RUN   TestNormalizeAttributes/Only_commas
=== RUN   TestNormalizeAttributes/Multiple_comma-separated_attributes
=== RUN   TestNormalizeAttributes/Case_preservation
=== RUN   TestNormalizeAttributes/Leading_and_trailing_spaces
=== RUN   TestNormalizeAttributes/Real-world_LDAP_attribute_examples
=== RUN   TestNormalizeAttributes/Jira-style_attribute_format
=== RUN   TestNormalizeAttributes/Single_string_with_single_attribute
=== RUN   TestNormalizeAttributes/Mix_of_standard_and_operational_attributes
--- PASS: TestNormalizeAttributes (0.00s)
    --- PASS: TestNormalizeAttributes/Empty_input (0.00s)
    --- PASS: TestNormalizeAttributes/No_commas (0.00s)
    --- PASS: TestNormalizeAttributes/Single_comma-separated_string (0.00s)
    --- PASS: TestNormalizeAttributes/Mixed_input (0.00s)
    --- PASS: TestNormalizeAttributes/With_spaces (0.00s)
    --- PASS: TestNormalizeAttributes/Empty_parts (0.00s)
    --- PASS: TestNormalizeAttributes/Single_element (0.00s)
    --- PASS: TestNormalizeAttributes/Only_commas (0.00s)
    --- PASS: TestNormalizeAttributes/Multiple_comma-separated_attributes (0.00s)
    --- PASS: TestNormalizeAttributes/Case_preservation (0.00s)
    --- PASS: TestNormalizeAttributes/Leading_and_trailing_spaces (0.00s)
    --- PASS: TestNormalizeAttributes/Real-world_LDAP_attribute_examples (0.00s)
    --- PASS: TestNormalizeAttributes/Jira-style_attribute_format (0.00s)
    --- PASS: TestNormalizeAttributes/Single_string_with_single_attribute (0.00s)
    --- PASS: TestNormalizeAttributes/Mix_of_standard_and_operational_attributes (0.00s)
PASS
ok      goauthentik.io/internal/outpost/ldap/search     0.194s
```
2025-06-11 18:16:40 +02:00
6ec745ddc0 website/integrations: standardize application slug placeholder in docs (#15007)
Standardizes application slug placeholder in docs
2025-06-11 16:52:21 +01:00
a44375a9d8 core: bump django from 5.1.10 to 5.1.11 (#14997) 2025-06-11 17:26:52 +02:00
3bf9cf681a web/elements: Add light mode custom css handling (#14944)
* fix(ui): Add light mode custom css handling

Signed-off-by: CodeMax IT Solutions Pvt. Ltd. <137166088+cdmx1@users.noreply.github.com>

* Update Base.ts

Signed-off-by: CodeMax IT Solutions Pvt. Ltd. <137166088+cdmx1@users.noreply.github.com>

---------

Signed-off-by: CodeMax IT Solutions Pvt. Ltd. <137166088+cdmx1@users.noreply.github.com>
2025-06-11 15:03:05 +02:00
b8cf0e5dc4 website/docs: add host header dynamic property mapping (#15006)
* Adds the property mapping and updates some language in the doc

* Typos
2025-06-11 13:50:18 +01:00
2180bdf7c2 core, web: update translations (#14999)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-06-11 12:35:18 +00:00
8af2d189d9 website/docs: fixes misplaced sentence (#14998)
fixes misplaced sentence

Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-06-11 07:17:03 -05:00
66f96a280e website/docs: note usage of is_restored by source stage (#13422) 2025-06-11 13:52:26 +02:00
09d5c6fa43 website: bump the build group in /website with 6 updates (#15001)
Bumps the build group in /website with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.31` | `1.12.0` |


Updates `@swc/core-darwin-arm64` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

Updates `@swc/core-linux-arm64-gnu` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

Updates `@swc/core-linux-x64-gnu` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

Updates `@swc/html-darwin-arm64` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

Updates `@swc/html-linux-arm64-gnu` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

Updates `@swc/html-linux-x64-gnu` from 1.11.31 to 1.12.0
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.31...v1.12.0)

---
updated-dependencies:
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  dependency-version: 1.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 12:23:47 +02:00
6eb4e78b22 web: bump @sentry/browser from 9.27.0 to 9.28.0 in /web in the sentry group across 1 directory (#15002)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.27.0 to 9.28.0
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.27.0...9.28.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 12:23:36 +02:00
c3eada8d33 core: bump msgraph-sdk from 1.32.0 to 1.33.0 (#15003)
Bumps [msgraph-sdk](https://github.com/microsoftgraph/msgraph-sdk-python) from 1.32.0 to 1.33.0.
- [Release notes](https://github.com/microsoftgraph/msgraph-sdk-python/releases)
- [Changelog](https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/microsoftgraph/msgraph-sdk-python/compare/v1.32.0...v1.33.0)

---
updated-dependencies:
- dependency-name: msgraph-sdk
  dependency-version: 1.33.0
  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>
2025-06-11 12:22:53 +02:00
48130a7463 core: bump google-api-python-client from 2.171.0 to 2.172.0 (#15004)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.171.0 to 2.172.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.171.0...v2.172.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-version: 2.172.0
  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>
2025-06-11 12:22:44 +02:00
9ffb2443f6 web/admin: fix language in certificate import (#14953)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/admin: fix language in certificate import modal

There are two buttons on the CertificateKeyPairList: "Create" and "Generate".  This language is confusing, as it is unclear what the difference between those two options could be.  The
second generates and stores a new key pair; the first only creates an entry in our database for an existing pair generated externally. After a short conversation, changing it to
"Import" seemed like a safe option.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

---------

Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 18:49:15 +02:00
0b4aed7a3d website/integrations: add new categories and update sidebar info (#14995)
* Added categories and moved docs

* Moved more docs

* move a couple more things

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

* auto-sort categories

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

* Updated sidebar info

* Add path to integrations file

* Moves meshcentral to device management. Moves cloudflare access and globalprotect to networking

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 17:18:09 +01:00
86dd1a96f6 brands: fix custom_css being escaped (#14994)
* brands: fix custom_css being escaped

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

* escape adequately

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 17:19:38 +02:00
0ce017b77e web/admin: show selected policy engine mode on bindings pages, allow setting it on sources (#12963)
* web/admin: show select policy engine mode on bindings pages, allow setting it in sources

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>

* slight cleanup

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 16:17:31 +02:00
e22e79f310 website/integrations: add bitwarden (#14922)
* Add doc and update sidebar

* WIP

* WIP

* SAML configuration complete

* Capitalisation

* Added OIDC instructions

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Applied Dominic's suggestions

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Applied suggestion from Dominic

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/bitwarden/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Applied Tana's suggestions

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-06-10 08:35:13 -05:00
ac575aecfa core: bump goauthentik.io/api/v3 from 3.2025061.1 to 3.2025061.2 (#14986)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025061.1 to 3.2025061.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.2025061.1...v3.2025061.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025061.2
  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>
2025-06-10 14:57:49 +02:00
fd61fb31b5 website: bump @types/node from 22.15.30 to 24.0.0 in /website (#14988)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.30 to 24.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.0.0
  dependency-type: direct:development
  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>
2025-06-10 14:57:42 +02:00
d7f6e5b79d website: bump the eslint group in /website with 3 updates (#14987)
Bumps the eslint group in /website with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/parser)

Updates `typescript-eslint` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 14:56:07 +02:00
09ded2a19a web: bump the eslint group across 2 directories with 3 updates (#14991)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 1 update in the /web directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `typescript-eslint` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/parser)

Updates `typescript-eslint` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.33.1 to 8.34.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/parser)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.0
  dependency-type: indirect
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 14:55:58 +02:00
6d4c9a3446 website/integrations: fix typos, update language and styling (#14978)
* Typo and improved language

* Changes "admin" to "administrator" and updates indentation

* Updates miniflux to newer styling

* Combines two notes at beginning of jellyfin doc into one

* Replaces all "your application slug" with "application_slug" and replaces tags that are no longer in use

* Replaces tags that are no longer in use

* Updates immich indentation, application_slug and removes tags

* Updated bookstack indentation, tags and application slug

* Removes kbd and em tags, and updates the application slug

* Gix metadata header in bookstack doc

* Lint fix miniflux

* ArgoCD indentation

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
2025-06-10 12:48:49 +01:00
74b5380f32 website/integrations: add 1password (#14815)
* Add doc and update sidebar

* Begin document

* WIP

* Typo

* Added automated user privisioning steps

* Added note about redirect URIs

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Updated based on recommendations from Dominic

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/1password/index.mdx

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-06-10 11:43:43 +00:00
c65b3e8ae5 website/integrations: add nextcloud ldap config and update doc to new styling (#14866)
* Initial changes

* Added LDAP config

* WIP

* WIP

* Updated document to new style and added LDAP config

* Make website

* WIP

* Fixed formatting on SAML section

* Typo

* Update website/integrations/services/nextcloud/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/nextcloud/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/nextcloud/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Update website/integrations/services/nextcloud/index.mdx

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Applied suggestions from Dominic

* Applied more suggestions

* Typo

* Applied suggestions

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-06-10 12:09:43 +01:00
88fa7e37dc outposts: Refactor session end signal and add LDAP support (#14539)
* outpost: promote session end signal to non-provider specific

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

* implement server-side logout in ldap

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

* fix previous import

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

* use better retry logic

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

* log

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

* make more generic if we switch from ws to something else

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

* make it possible to e2e test WS

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

* fix ldap session id

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

* ok I actually need to go to bed this took me an hour to fix

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

* format; add ldap test

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

* fix leftover state

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

* remove thread

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

* use ws base for radius

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

* separate test utils

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

* rename

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

* fix missing super calls

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

* websocket tests with browser 🎉

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

* add proxy test for sign out

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

* fix install_id issue with channels tests

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

* fix proxy basic auth test

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

* big code dedupe

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

* allow passing go build args

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

* improve waiting for outpost

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

* rewrite ldap tests

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

* ok actually fix the tests

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

* undo a couple things that need more time to cook

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

* remove unused lockfile-lint dependency since we use a shell script and SFE does not have a lockfile

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

* fix session id for ldap

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

* fix missing createTimestamp and modifyTimestamp ldap attributes

closes #10474

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 12:11:21 +02:00
8bfc9ab7c9 core: bump cryptography from 45.0.3 to 45.0.4 (#14989)
Bumps [cryptography](https://github.com/pyca/cryptography) from 45.0.3 to 45.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/45.0.3...45.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 45.0.4
  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>
2025-06-10 11:25:52 +02:00
f145580dae core: bump goauthentik/fips-python from 3.13.3-slim-bookworm-fips to 3.13.4-slim-bookworm-fips (#14990)
core: bump goauthentik/fips-python

Bumps goauthentik/fips-python from 3.13.3-slim-bookworm-fips to 3.13.4-slim-bookworm-fips.

---
updated-dependencies:
- dependency-name: goauthentik/fips-python
  dependency-version: 3.13.4-slim-bookworm-fips
  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>
2025-06-10 11:25:33 +02:00
31eb4a6315 web: bump API Client version (#14985)
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>
2025-06-10 02:39:20 +02:00
48696e3d7d core, web: update translations (#14984)
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>
2025-06-10 02:36:27 +02:00
734db4dee6 events: rework metrics endpoint (#14934)
* rework event volume

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

* migrate more

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

* migrate more

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

* migrate more

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

* the rest of the owl

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

* client-side data padding

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

* I love deleting code

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

* fix

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

* fix clamping

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

* chunk it

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

* fix

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

* fix tests

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

* add event-to-color map

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

* sync colours

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

* switch colours

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

* heatmap?

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

* Revert "heatmap?"

This reverts commit c1f549a18b.

* Revert "Revert "heatmap?""

This reverts commit 6d6175b96b.

* Revert "Revert "Revert "heatmap?"""

This reverts commit 3717903f12.

* format

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-10 02:36:09 +02:00
856ac052e7 website/integrations: replaces all kbd and em tags (#14980)
Replaces all kbd and em tags
2025-06-09 21:06:37 +01:00
dea2d67ceb internal/outpost: fix incorrect usage of golang SHA API (#14981)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-09 20:57:36 +02:00
a844fb41d4 web/elements: fix dual select without sortBy (#14977)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-09 19:04:07 +02:00
f4da22aea8 website/docs: added a link in our Upgrade docs to the Outpost upgrade docs, slight reformatting (#14931)
* add link to outpost upgrade doc

* darn build breaks

* kens edits

* again

---------

Co-authored-by: Tana M Berry <tana@goauthentik.io>
2025-06-09 10:14:43 -05:00
1464852b92 website: fix search across multiple subdomains (#14976)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-09 17:13:04 +02:00
6aebf45aea core: bump goauthentik.io/api/v3 from 3.2025060.1 to 3.2025061.1 (#14972)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2025060.1 to 3.2025061.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.2025060.1...v3.2025061.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-version: 3.2025061.1
  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>
2025-06-09 14:52:58 +02:00
fad6ac76af web: bump API Client version (#14971) 2025-06-09 02:43:50 +00:00
c60a145f95 root: backport 2025.6.1 bump (#14970)
release: 2025.6.1

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-06-09 04:15:33 +02:00
652a32d968 stages/email: Only attach logo to email if used (#14835)
* Only attach logo to email if used

* Fix MIME logo attachment to adhere to standard

* format, fix web

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

* not tuple

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-09 03:49:00 +02:00
bd5a66a3c7 web: bump @codemirror/lang-python from 6.1.6 to 6.2.1 in /web (#14713)
Bumps [@codemirror/lang-python](https://github.com/codemirror/lang-python) from 6.1.6 to 6.2.1.
- [Changelog](https://github.com/codemirror/lang-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/lang-python/compare/6.1.6...6.2.1)

---
updated-dependencies:
- dependency-name: "@codemirror/lang-python"
  dependency-version: 6.2.1
  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>
2025-06-09 03:46:41 +02:00
a4c4b07614 website: bump prettier-plugin-packagejson from 2.5.14 to 2.5.15 in /website (#14829)
website: bump prettier-plugin-packagejson in /website

Bumps [prettier-plugin-packagejson](https://github.com/matzkoh/prettier-plugin-packagejson) from 2.5.14 to 2.5.15.
- [Release notes](https://github.com/matzkoh/prettier-plugin-packagejson/releases)
- [Commits](https://github.com/matzkoh/prettier-plugin-packagejson/compare/v2.5.14...v2.5.15)

---
updated-dependencies:
- dependency-name: prettier-plugin-packagejson
  dependency-version: 2.5.15
  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>
2025-06-09 03:39:00 +02:00
aaf76bab92 core: bump selenium/standalone-chrome from 136.0 to 137.0 in /tests/e2e (#14963)
Bumps selenium/standalone-chrome from 136.0 to 137.0.

---
updated-dependencies:
- dependency-name: selenium/standalone-chrome
  dependency-version: '137.0'
  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>
2025-06-09 03:32:59 +02:00
814f3fc43d core: bump axllent/mailpit from v1.25.1 to v1.26.0 in /tests/e2e (#14964)
Bumps axllent/mailpit from v1.25.1 to v1.26.0.

---
updated-dependencies:
- dependency-name: axllent/mailpit
  dependency-version: v1.26.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 03:32:51 +02:00
d017af1347 core: bump astral-sh/uv from 0.7.11 to 0.7.12 (#14965)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.7.11 to 0.7.12.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.7.11...0.7.12)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.7.12
  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>
2025-06-09 03:32:36 +02:00
6e11554f62 core: bump github.com/redis/go-redis/v9 from 9.9.0 to 9.10.0 (#14966)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.9.0 to 9.10.0.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.9.0...v9.10.0)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-version: 9.10.0
  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>
2025-06-09 03:32:27 +02:00
14f2edc04b web: bump @types/mocha from 10.0.8 to 10.0.10 in /web (#14684)
Bumps [@types/mocha](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mocha) from 10.0.8 to 10.0.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/mocha)

---
updated-dependencies:
- dependency-name: "@types/mocha"
  dependency-version: 10.0.10
  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>
2025-06-09 03:30:46 +02:00
9f6173d8b2 web: bump ts-pattern from 5.4.0 to 5.7.1 in /web (#14686)
Bumps [ts-pattern](https://github.com/gvergnaud/ts-pattern) from 5.4.0 to 5.7.1.
- [Release notes](https://github.com/gvergnaud/ts-pattern/releases)
- [Commits](https://github.com/gvergnaud/ts-pattern/compare/v5.4.0...v5.7.1)

---
updated-dependencies:
- dependency-name: ts-pattern
  dependency-version: 5.7.1
  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>
2025-06-09 03:30:29 +02:00
52c3ba551d website: bump @types/node from 22.15.29 to 22.15.30 in /website (#14968)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.29 to 22.15.30.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.30
  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>
2025-06-09 03:30:02 +02:00
633c6ff245 web: bump mermaid from 11.4.1 to 11.6.0 in /web (#14688)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.4.1 to 11.6.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.4.1...mermaid@11.6.0)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.6.0
  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>
2025-06-09 03:28:44 +02:00
e5cb925f70 web: bump @fortawesome/fontawesome-free from 6.6.0 to 6.7.2 in /web (#14716)
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 6.6.0 to 6.7.2.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.6.0...6.7.2)

---
updated-dependencies:
- dependency-name: "@fortawesome/fontawesome-free"
  dependency-version: 6.7.2
  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>
2025-06-09 03:28:18 +02:00
a0b1327456 web: bump the eslint group across 2 directories with 3 updates (#14833)
Bumps the eslint group with 1 update in the /packages/eslint-config directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).
Bumps the eslint group with 1 update in the /web directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `typescript-eslint` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/parser)

Updates `typescript-eslint` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/typescript-eslint)

Updates `@typescript-eslint/eslint-plugin` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.33.0 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/parser)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.33.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.33.1
  dependency-type: indirect
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 03:26:02 +02:00
891907390b website: bump the eslint group in /website with 4 updates (#14967)
Bumps the eslint group in /website with 4 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [eslint](https://github.com/eslint/eslint) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `@eslint/js` from 9.27.0 to 9.28.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/commits/v9.28.0/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.32.1 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/eslint-plugin)

Updates `eslint` from 9.27.0 to 9.28.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.27.0...v9.28.0)

Updates `typescript-eslint` from 8.32.1 to 8.33.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.28.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.28.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 03:14:20 +02:00
e1c47c0f00 website: bump @typescript-eslint/parser from 8.32.1 to 8.33.1 in /website (#14828)
* website: bump @typescript-eslint/parser in /website

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.32.1 to 8.33.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

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

* update dependabot groups for website

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>
2025-06-09 03:03:49 +02:00
0b71aa43d1 core, web: update translations (#14962)
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>
2025-06-09 03:01:25 +02:00
b4d26d5092 web: bump @sentry/browser from 9.24.0 to 9.25.1 in /web in the sentry group across 1 directory (#14865)
web: bump @sentry/browser in /web in the sentry group across 1 directory

Bumps the sentry group with 1 update in the /web directory: [@sentry/browser](https://github.com/getsentry/sentry-javascript).


Updates `@sentry/browser` from 9.24.0 to 9.25.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/9.24.0...9.25.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 9.25.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: sentry
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 02:11:29 +02:00
8a9f6fb1ce core, web: update translations (#14954)
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>
2025-06-09 02:11:11 +02:00
355f302cb7 lifecycle/aws: bump aws-cdk from 2.1017.1 to 2.1018.0 in /lifecycle/aws (#14939)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1017.1 to 2.1018.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1018.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1018.0
  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>
2025-06-09 02:10:16 +02:00
11666f5658 core: bump golang.org/x/sync from 0.14.0 to 0.15.0 (#14936)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/sync/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-version: 0.15.0
  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>
2025-06-09 02:09:41 +02:00
d54fe15511 website: bump the build group across 1 directory with 9 updates (#14937)
Bumps the build group with 9 updates in the /website directory:

| Package | From | To |
| --- | --- | --- |
| [@rspack/binding-darwin-arm64](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.3.11` | `1.3.15` |
| [@rspack/binding-linux-arm64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.3.11` | `1.3.15` |
| [@rspack/binding-linux-x64-gnu](https://github.com/web-infra-dev/rspack/tree/HEAD/packages/rspack) | `1.3.11` | `1.3.15` |
| [@swc/core-darwin-arm64](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |
| [@swc/core-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |
| [@swc/core-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |
| [@swc/html-darwin-arm64](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |
| [@swc/html-linux-arm64-gnu](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |
| [@swc/html-linux-x64-gnu](https://github.com/swc-project/swc) | `1.11.29` | `1.11.31` |



Updates `@rspack/binding-darwin-arm64` from 1.3.11 to 1.3.15
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.3.15/packages/rspack)

Updates `@rspack/binding-linux-arm64-gnu` from 1.3.11 to 1.3.15
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.3.15/packages/rspack)

Updates `@rspack/binding-linux-x64-gnu` from 1.3.11 to 1.3.15
- [Release notes](https://github.com/web-infra-dev/rspack/releases)
- [Commits](https://github.com/web-infra-dev/rspack/commits/v1.3.15/packages/rspack)

Updates `@swc/core-darwin-arm64` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

Updates `@swc/core-linux-arm64-gnu` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

Updates `@swc/core-linux-x64-gnu` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

Updates `@swc/html-darwin-arm64` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

Updates `@swc/html-linux-arm64-gnu` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

Updates `@swc/html-linux-x64-gnu` from 1.11.29 to 1.11.31
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/compare/v1.11.29...v1.11.31)

---
updated-dependencies:
- dependency-name: "@rspack/binding-darwin-arm64"
  dependency-version: 1.3.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-arm64-gnu"
  dependency-version: 1.3.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@rspack/binding-linux-x64-gnu"
  dependency-version: 1.3.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-darwin-arm64"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-arm64-gnu"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
- dependency-name: "@swc/html-linux-x64-gnu"
  dependency-version: 1.11.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: build
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 02:06:18 +02:00
310 changed files with 12589 additions and 11386 deletions

View File

@ -1,16 +1,16 @@
[bumpversion] [bumpversion]
current_version = 2025.6.0 current_version = 2025.6.2
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
serialize = serialize =
{major}.{minor}.{patch}-{rc_t}{rc_n} {major}.{minor}.{patch}-{rc_t}{rc_n}
{major}.{minor}.{patch} {major}.{minor}.{patch}
message = release: {new_version} message = release: {new_version}
tag_name = version/{new_version} tag_name = version/{new_version}
[bumpversion:part:rc_t] [bumpversion:part:rc_t]
values = values =
rc rc
final final
optional_value = final optional_value = final

View File

@ -11,3 +11,4 @@ blueprints/local
!gen-ts-api/node_modules !gen-ts-api/node_modules
!gen-ts-api/dist/** !gen-ts-api/dist/**
!gen-go-api/ !gen-go-api/
.venv

View File

@ -100,6 +100,13 @@ updates:
goauthentik: goauthentik:
patterns: patterns:
- "@goauthentik/*" - "@goauthentik/*"
eslint:
patterns:
- "@eslint/*"
- "@typescript-eslint/*"
- "eslint-*"
- "eslint"
- "typescript-eslint"
- package-ecosystem: npm - package-ecosystem: npm
directory: "/lifecycle/aws" directory: "/lifecycle/aws"
schedule: schedule:

View File

@ -202,7 +202,7 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: web/dist path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
- name: prepare web ui - name: prepare web ui
if: steps.cache-web.outputs.cache-hit != 'true' if: steps.cache-web.outputs.cache-hit != 'true'
working-directory: web working-directory: web

View File

@ -75,9 +75,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0" /bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 4: Download uv # Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.7.11 AS uv FROM ghcr.io/astral-sh/uv:0.7.13 AS uv
# Stage 5: Base python image # Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.3-slim-bookworm-fips AS python-base FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base
ENV VENV_PATH="/ak-root/.venv" \ ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \ PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \

View File

@ -94,7 +94,7 @@ gen-build: ## Extract the schema from the database
AUTHENTIK_DEBUG=true \ AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \ AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \ AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
uv run ak make_blueprint_schema > blueprints/schema.json uv run ak make_blueprint_schema --file blueprints/schema.json
AUTHENTIK_DEBUG=true \ AUTHENTIK_DEBUG=true \
AUTHENTIK_TENANTS__ENABLED=true \ AUTHENTIK_TENANTS__ENABLED=true \
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \ AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \

View File

@ -2,7 +2,7 @@
from os import environ from os import environ
__version__ = "2025.6.0" __version__ = "2025.6.2"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -1,79 +0,0 @@
"""authentik administration metrics"""
from datetime import timedelta
from django.db.models.functions import ExtractHour
from drf_spectacular.utils import extend_schema, extend_schema_field
from guardian.shortcuts import get_objects_for_user
from rest_framework.fields import IntegerField, SerializerMethodField
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from authentik.core.api.utils import PassiveSerializer
from authentik.events.models import EventAction
class CoordinateSerializer(PassiveSerializer):
"""Coordinates for diagrams"""
x_cord = IntegerField(read_only=True)
y_cord = IntegerField(read_only=True)
class LoginMetricsSerializer(PassiveSerializer):
"""Login Metrics per 1h"""
logins = SerializerMethodField()
logins_failed = SerializerMethodField()
authorizations = SerializerMethodField()
@extend_schema_field(CoordinateSerializer(many=True))
def get_logins(self, _):
"""Get successful logins per 8 hours for the last 7 days"""
user = self.context["user"]
return (
get_objects_for_user(user, "authentik_events.view_event").filter(
action=EventAction.LOGIN
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
@extend_schema_field(CoordinateSerializer(many=True))
def get_logins_failed(self, _):
"""Get failed logins per 8 hours for the last 7 days"""
user = self.context["user"]
return (
get_objects_for_user(user, "authentik_events.view_event").filter(
action=EventAction.LOGIN_FAILED
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
@extend_schema_field(CoordinateSerializer(many=True))
def get_authorizations(self, _):
"""Get successful authorizations per 8 hours for the last 7 days"""
user = self.context["user"]
return (
get_objects_for_user(user, "authentik_events.view_event").filter(
action=EventAction.AUTHORIZE_APPLICATION
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
class AdministrationMetricsViewSet(APIView):
"""Login Metrics per 1h"""
permission_classes = [IsAuthenticated]
@extend_schema(responses={200: LoginMetricsSerializer(many=False)})
def get(self, request: Request) -> Response:
"""Login Metrics per 1h"""
serializer = LoginMetricsSerializer(True)
serializer.context["user"] = request.user
return Response(serializer.data)

View File

@ -36,11 +36,6 @@ class TestAdminAPI(TestCase):
body = loads(response.content) body = loads(response.content)
self.assertEqual(len(body), 0) self.assertEqual(len(body), 0)
def test_metrics(self):
"""Test metrics API"""
response = self.client.get(reverse("authentik_api:admin_metrics"))
self.assertEqual(response.status_code, 200)
def test_apps(self): def test_apps(self):
"""Test apps API""" """Test apps API"""
response = self.client.get(reverse("authentik_api:apps-list")) response = self.client.get(reverse("authentik_api:apps-list"))

View File

@ -3,7 +3,6 @@
from django.urls import path from django.urls import path
from authentik.admin.api.meta import AppsViewSet, ModelViewSet from authentik.admin.api.meta import AppsViewSet, ModelViewSet
from authentik.admin.api.metrics import AdministrationMetricsViewSet
from authentik.admin.api.system import SystemView from authentik.admin.api.system import SystemView
from authentik.admin.api.version import VersionView from authentik.admin.api.version import VersionView
from authentik.admin.api.version_history import VersionHistoryViewSet from authentik.admin.api.version_history import VersionHistoryViewSet
@ -12,11 +11,6 @@ from authentik.admin.api.workers import WorkerView
api_urlpatterns = [ api_urlpatterns = [
("admin/apps", AppsViewSet, "apps"), ("admin/apps", AppsViewSet, "apps"),
("admin/models", ModelViewSet, "models"), ("admin/models", ModelViewSet, "models"),
path(
"admin/metrics/",
AdministrationMetricsViewSet.as_view(),
name="admin_metrics",
),
path("admin/version/", VersionView.as_view(), name="admin_version"), path("admin/version/", VersionView.as_view(), name="admin_version"),
("admin/version/history", VersionHistoryViewSet, "version_history"), ("admin/version/history", VersionHistoryViewSet, "version_history"),
path("admin/workers/", WorkerView.as_view(), name="admin_workers"), path("admin/workers/", WorkerView.as_view(), name="admin_workers"),

View File

@ -72,20 +72,33 @@ class Command(BaseCommand):
"additionalProperties": True, "additionalProperties": True,
}, },
"entries": { "entries": {
"type": "array", "anyOf": [
"items": { {
"oneOf": [], "type": "array",
}, "items": {"$ref": "#/$defs/blueprint_entry"},
},
{
"type": "object",
"additionalProperties": {
"type": "array",
"items": {"$ref": "#/$defs/blueprint_entry"},
},
},
],
}, },
}, },
"$defs": {}, "$defs": {"blueprint_entry": {"oneOf": []}},
} }
def add_arguments(self, parser):
parser.add_argument("--file", type=str)
@no_translations @no_translations
def handle(self, *args, **options): def handle(self, *args, file: str, **options):
"""Generate JSON Schema for blueprints""" """Generate JSON Schema for blueprints"""
self.build() self.build()
self.stdout.write(dumps(self.schema, indent=4, default=Command.json_default)) with open(file, "w") as _schema:
_schema.write(dumps(self.schema, indent=4, default=Command.json_default))
@staticmethod @staticmethod
def json_default(value: Any) -> Any: def json_default(value: Any) -> Any:
@ -112,7 +125,7 @@ class Command(BaseCommand):
} }
) )
model_path = f"{model._meta.app_label}.{model._meta.model_name}" model_path = f"{model._meta.app_label}.{model._meta.model_name}"
self.schema["properties"]["entries"]["items"]["oneOf"].append( self.schema["$defs"]["blueprint_entry"]["oneOf"].append(
self.template_entry(model_path, model, serializer) self.template_entry(model_path, model, serializer)
) )
@ -134,7 +147,7 @@ class Command(BaseCommand):
"id": {"type": "string"}, "id": {"type": "string"},
"state": { "state": {
"type": "string", "type": "string",
"enum": [s.value for s in BlueprintEntryDesiredState], "enum": sorted([s.value for s in BlueprintEntryDesiredState]),
"default": "present", "default": "present",
}, },
"conditions": {"type": "array", "items": {"type": "boolean"}}, "conditions": {"type": "array", "items": {"type": "boolean"}},
@ -205,7 +218,7 @@ class Command(BaseCommand):
"type": "object", "type": "object",
"required": ["permission"], "required": ["permission"],
"properties": { "properties": {
"permission": {"type": "string", "enum": perms}, "permission": {"type": "string", "enum": sorted(perms)},
"user": {"type": "integer"}, "user": {"type": "integer"},
"role": {"type": "string"}, "role": {"type": "string"},
}, },

View File

@ -1,10 +1,11 @@
version: 1 version: 1
entries: entries:
- identifiers: foo:
name: "%(id)s" - identifiers:
slug: "%(id)s" name: "%(id)s"
model: authentik_flows.flow slug: "%(id)s"
state: present model: authentik_flows.flow
attrs: state: present
designation: stage_configuration attrs:
title: foo designation: stage_configuration
title: foo

View File

@ -191,11 +191,18 @@ class Blueprint:
"""Dataclass used for a full export""" """Dataclass used for a full export"""
version: int = field(default=1) version: int = field(default=1)
entries: list[BlueprintEntry] = field(default_factory=list) entries: list[BlueprintEntry] | dict[str, list[BlueprintEntry]] = field(default_factory=list)
context: dict = field(default_factory=dict) context: dict = field(default_factory=dict)
metadata: BlueprintMetadata | None = field(default=None) metadata: BlueprintMetadata | None = field(default=None)
def iter_entries(self) -> Iterable[BlueprintEntry]:
if isinstance(self.entries, dict):
for _section, entries in self.entries.items():
yield from entries
else:
yield from self.entries
class YAMLTag: class YAMLTag:
"""Base class for all YAML Tags""" """Base class for all YAML Tags"""
@ -226,7 +233,7 @@ class KeyOf(YAMLTag):
self.id_from = node.value self.id_from = node.value
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any: def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
for _entry in blueprint.entries: for _entry in blueprint.iter_entries():
if _entry.id == self.id_from and _entry._state.instance: if _entry.id == self.id_from and _entry._state.instance:
# Special handling for PolicyBindingModels, as they'll have a different PK # Special handling for PolicyBindingModels, as they'll have a different PK
# which is used when creating policy bindings # which is used when creating policy bindings

View File

@ -384,7 +384,7 @@ class Importer:
def _apply_models(self, raise_errors=False) -> bool: def _apply_models(self, raise_errors=False) -> bool:
"""Apply (create/update) models yaml""" """Apply (create/update) models yaml"""
self.__pk_map = {} self.__pk_map = {}
for entry in self._import.entries: for entry in self._import.iter_entries():
model_app_label, model_name = entry.get_model(self._import).split(".") model_app_label, model_name = entry.get_model(self._import).split(".")
try: try:
model: type[SerializerModel] = registry.get_model(model_app_label, model_name) model: type[SerializerModel] = registry.get_model(model_app_label, model_name)

View File

@ -47,7 +47,7 @@ class MetaModelRegistry:
models = apps.get_models() models = apps.get_models()
for _, value in self.models.items(): for _, value in self.models.items():
models.append(value) models.append(value)
return models return sorted(models, key=str)
def get_model(self, app_label: str, model_id: str) -> type[Model]: def get_model(self, app_label: str, model_id: str) -> type[Model]:
"""Get model checks if any virtual models are registered, and falls back """Get model checks if any virtual models are registered, and falls back

View File

@ -148,3 +148,14 @@ class TestBrands(APITestCase):
"default_locale": "", "default_locale": "",
}, },
) )
def test_custom_css(self):
"""Test custom_css"""
brand = create_test_brand()
brand.branding_custom_css = """* {
font-family: "Foo bar";
}"""
brand.save()
res = self.client.get(reverse("authentik_core:if-user"))
self.assertEqual(res.status_code, 200)
self.assertIn(brand.branding_custom_css, res.content.decode())

View File

@ -5,6 +5,8 @@ from typing import Any
from django.db.models import F, Q from django.db.models import F, Q
from django.db.models import Value as V from django.db.models import Value as V
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.utils.html import _json_script_escapes
from django.utils.safestring import mark_safe
from authentik import get_full_version from authentik import get_full_version
from authentik.brands.models import Brand from authentik.brands.models import Brand
@ -32,8 +34,13 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
"""Context Processor that injects brand object into every template""" """Context Processor that injects brand object into every template"""
brand = getattr(request, "brand", DEFAULT_BRAND) brand = getattr(request, "brand", DEFAULT_BRAND)
tenant = getattr(request, "tenant", Tenant()) tenant = getattr(request, "tenant", Tenant())
# similarly to `json_script` we escape everything HTML-related, however django
# only directly exposes this as a function that also wraps it in a <script> tag
# which we dont want for CSS
brand_css = mark_safe(str(brand.branding_custom_css).translate(_json_script_escapes)) # nosec
return { return {
"brand": brand, "brand": brand,
"brand_css": brand_css,
"footer_links": tenant.footer_links, "footer_links": tenant.footer_links,
"html_meta": {**get_http_meta()}, "html_meta": {**get_http_meta()},
"version": get_full_version(), "version": get_full_version(),

View File

@ -2,11 +2,9 @@
from collections.abc import Iterator from collections.abc import Iterator
from copy import copy from copy import copy
from datetime import timedelta
from django.core.cache import cache from django.core.cache import cache
from django.db.models import QuerySet from django.db.models import QuerySet
from django.db.models.functions import ExtractHour
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
@ -20,7 +18,6 @@ from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.api.pagination import Pagination from authentik.api.pagination import Pagination
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.providers import ProviderSerializer from authentik.core.api.providers import ProviderSerializer
@ -28,7 +25,6 @@ from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer from authentik.core.api.utils import ModelSerializer
from authentik.core.models import Application, User from authentik.core.models import Application, User
from authentik.events.logs import LogEventSerializer, capture_logs from authentik.events.logs import LogEventSerializer, capture_logs
from authentik.events.models import EventAction
from authentik.lib.utils.file import ( from authentik.lib.utils.file import (
FilePathSerializer, FilePathSerializer,
FileUploadSerializer, FileUploadSerializer,
@ -321,18 +317,3 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
"""Set application icon (as URL)""" """Set application icon (as URL)"""
app: Application = self.get_object() app: Application = self.get_object()
return set_file_url(request, app, "meta_icon") return set_file_url(request, app, "meta_icon")
@permission_required("authentik_core.view_application", ["authentik_events.view_event"])
@extend_schema(responses={200: CoordinateSerializer(many=True)})
@action(detail=True, pagination_class=None, filter_backends=[])
def metrics(self, request: Request, slug: str):
"""Metrics for application logins"""
app = self.get_object()
return Response(
get_objects_for_user(request.user, "authentik_events.view_event").filter(
action=EventAction.AUTHORIZE_APPLICATION,
context__authorized_application__pk=app.pk.hex,
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)

View File

@ -6,7 +6,6 @@ from typing import Any
from django.contrib.auth import update_session_auth_hash from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.db.models.functions import ExtractHour
from django.db.transaction import atomic from django.db.transaction import atomic
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from django.urls import reverse_lazy from django.urls import reverse_lazy
@ -52,7 +51,6 @@ from rest_framework.validators import UniqueValidator
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.brands.models import Brand from authentik.brands.models import Brand
from authentik.core.api.used_by import UsedByMixin from authentik.core.api.used_by import UsedByMixin
@ -317,53 +315,6 @@ class SessionUserSerializer(PassiveSerializer):
original = UserSelfSerializer(required=False) original = UserSelfSerializer(required=False)
class UserMetricsSerializer(PassiveSerializer):
"""User Metrics"""
logins = SerializerMethodField()
logins_failed = SerializerMethodField()
authorizations = SerializerMethodField()
@extend_schema_field(CoordinateSerializer(many=True))
def get_logins(self, _):
"""Get successful logins per 8 hours for the last 7 days"""
user = self.context["user"]
request = self.context["request"]
return (
get_objects_for_user(request.user, "authentik_events.view_event").filter(
action=EventAction.LOGIN, user__pk=user.pk
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
@extend_schema_field(CoordinateSerializer(many=True))
def get_logins_failed(self, _):
"""Get failed logins per 8 hours for the last 7 days"""
user = self.context["user"]
request = self.context["request"]
return (
get_objects_for_user(request.user, "authentik_events.view_event").filter(
action=EventAction.LOGIN_FAILED, context__username=user.username
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
@extend_schema_field(CoordinateSerializer(many=True))
def get_authorizations(self, _):
"""Get failed logins per 8 hours for the last 7 days"""
user = self.context["user"]
request = self.context["request"]
return (
get_objects_for_user(request.user, "authentik_events.view_event").filter(
action=EventAction.AUTHORIZE_APPLICATION, user__pk=user.pk
)
# 3 data points per day, so 8 hour spans
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
)
class UsersFilter(FilterSet): class UsersFilter(FilterSet):
"""Filter for users""" """Filter for users"""
@ -607,17 +558,6 @@ class UserViewSet(UsedByMixin, ModelViewSet):
update_session_auth_hash(self.request, user) update_session_auth_hash(self.request, user)
return Response(status=204) return Response(status=204)
@permission_required("authentik_core.view_user", ["authentik_events.view_event"])
@extend_schema(responses={200: UserMetricsSerializer(many=False)})
@action(detail=True, pagination_class=None, filter_backends=[])
def metrics(self, request: Request, pk: int) -> Response:
"""User metrics per 1h"""
user: User = self.get_object()
serializer = UserMetricsSerializer(instance={})
serializer.context["user"] = user
serializer.context["request"] = request
return Response(serializer.data)
@permission_required("authentik_core.reset_user_password") @permission_required("authentik_core.reset_user_password")
@extend_schema( @extend_schema(
responses={ responses={

View File

@ -16,7 +16,7 @@
{% block head_before %} {% block head_before %}
{% endblock %} {% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
<style>{{ brand.branding_custom_css }}</style> <style>{{ brand_css }}</style>
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script> <script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
<script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script> <script src="{% versioned_script 'dist/standalone/loading/index-%v.js' %}" type="module"></script>
{% block head %} {% block head %}

View File

@ -81,22 +81,6 @@ class TestUsersAPI(APITestCase):
response = self.client.get(reverse("authentik_api:user-list"), {"include_groups": "true"}) response = self.client.get(reverse("authentik_api:user-list"), {"include_groups": "true"})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_metrics(self):
"""Test user's metrics"""
self.client.force_login(self.admin)
response = self.client.get(
reverse("authentik_api:user-metrics", kwargs={"pk": self.user.pk})
)
self.assertEqual(response.status_code, 200)
def test_metrics_denied(self):
"""Test user's metrics (non-superuser)"""
self.client.force_login(self.user)
response = self.client.get(
reverse("authentik_api:user-metrics", kwargs={"pk": self.user.pk})
)
self.assertEqual(response.status_code, 403)
def test_recovery_no_flow(self): def test_recovery_no_flow(self):
"""Test user recovery link (no recovery flow set)""" """Test user recovery link (no recovery flow set)"""
self.client.force_login(self.admin) self.client.force_login(self.admin)

View File

@ -1,28 +1,36 @@
"""Events API Views""" """Events API Views"""
from datetime import timedelta from datetime import timedelta
from json import loads
import django_filters import django_filters
from django.db.models.aggregates import Count from django.db.models import Count, ExpressionWrapper, F, QuerySet
from django.db.models import DateTimeField as DjangoDateTimeField
from django.db.models.fields.json import KeyTextTransform, KeyTransform from django.db.models.fields.json import KeyTextTransform, KeyTransform
from django.db.models.functions import ExtractDay, ExtractHour from django.db.models.functions import TruncHour
from django.db.models.query_utils import Q from django.db.models.query_utils import Q
from django.utils.timezone import now
from drf_spectacular.types import OpenApiTypes from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema from drf_spectacular.utils import OpenApiParameter, extend_schema
from guardian.shortcuts import get_objects_for_user from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import DictField, IntegerField from rest_framework.fields import ChoiceField, DateTimeField, DictField, IntegerField
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.core.api.object_types import TypeCreateSerializer from authentik.core.api.object_types import TypeCreateSerializer
from authentik.core.api.utils import ModelSerializer, PassiveSerializer from authentik.core.api.utils import ModelSerializer, PassiveSerializer
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
class EventVolumeSerializer(PassiveSerializer):
"""Count of events of action created on day"""
action = ChoiceField(choices=EventAction.choices)
time = DateTimeField()
count = IntegerField()
class EventSerializer(ModelSerializer): class EventSerializer(ModelSerializer):
"""Event Serializer""" """Event Serializer"""
@ -53,7 +61,7 @@ class EventsFilter(django_filters.FilterSet):
"""Filter for events""" """Filter for events"""
username = django_filters.CharFilter( username = django_filters.CharFilter(
field_name="user", lookup_expr="username", label="Username" field_name="user", label="Username", method="filter_username"
) )
context_model_pk = django_filters.CharFilter( context_model_pk = django_filters.CharFilter(
field_name="context", field_name="context",
@ -78,12 +86,19 @@ class EventsFilter(django_filters.FilterSet):
field_name="action", field_name="action",
lookup_expr="icontains", lookup_expr="icontains",
) )
actions = django_filters.MultipleChoiceFilter(
field_name="action",
choices=EventAction.choices,
)
brand_name = django_filters.CharFilter( brand_name = django_filters.CharFilter(
field_name="brand", field_name="brand",
lookup_expr="name", lookup_expr="name",
label="Brand name", label="Brand name",
) )
def filter_username(self, queryset, name, value):
return queryset.filter(Q(user__username=value) | Q(context__username=value))
def filter_context_model_pk(self, queryset, name, value): def filter_context_model_pk(self, queryset, name, value):
"""Because we store the PK as UUID.hex, """Because we store the PK as UUID.hex,
we need to remove the dashes that a client may send. We can't use a we need to remove the dashes that a client may send. We can't use a
@ -156,45 +171,37 @@ class EventViewSet(ModelViewSet):
return Response(EventTopPerUserSerializer(instance=events, many=True).data) return Response(EventTopPerUserSerializer(instance=events, many=True).data)
@extend_schema( @extend_schema(
responses={200: CoordinateSerializer(many=True)}, responses={200: EventVolumeSerializer(many=True)},
)
@action(detail=False, methods=["GET"], pagination_class=None)
def volume(self, request: Request) -> Response:
"""Get event volume for specified filters and timeframe"""
queryset = self.filter_queryset(self.get_queryset())
return Response(queryset.get_events_per(timedelta(days=7), ExtractHour, 7 * 3))
@extend_schema(
responses={200: CoordinateSerializer(many=True)},
filters=[],
parameters=[ parameters=[
OpenApiParameter( OpenApiParameter(
"action", "history_days",
type=OpenApiTypes.STR, type=OpenApiTypes.NUMBER,
location=OpenApiParameter.QUERY,
required=False,
),
OpenApiParameter(
"query",
type=OpenApiTypes.STR,
location=OpenApiParameter.QUERY, location=OpenApiParameter.QUERY,
required=False, required=False,
default=7,
), ),
], ],
) )
@action(detail=False, methods=["GET"], pagination_class=None) @action(detail=False, methods=["GET"], pagination_class=None)
def per_month(self, request: Request): def volume(self, request: Request) -> Response:
"""Get the count of events per month""" """Get event volume for specified filters and timeframe"""
filtered_action = request.query_params.get("action", EventAction.LOGIN) queryset: QuerySet[Event] = self.filter_queryset(self.get_queryset())
try: delta = timedelta(days=7)
query = loads(request.query_params.get("query", "{}")) time_delta = request.query_params.get("history_days", 7)
except ValueError: if time_delta:
return Response(status=400) delta = timedelta(days=min(int(time_delta), 60))
return Response( return Response(
get_objects_for_user(request.user, "authentik_events.view_event") queryset.filter(created__gte=now() - delta)
.filter(action=filtered_action) .annotate(hour=TruncHour("created"))
.filter(**query) .annotate(
.get_events_per(timedelta(weeks=4), ExtractDay, 30) time=ExpressionWrapper(
F("hour") - (F("hour__hour") % 6) * timedelta(hours=1),
output_field=DjangoDateTimeField(),
)
)
.values("time", "action")
.annotate(count=Count("pk"))
.order_by("time", "action")
) )
@extend_schema(responses={200: TypeCreateSerializer(many=True)}) @extend_schema(responses={200: TypeCreateSerializer(many=True)})

View File

@ -1,7 +1,5 @@
"""authentik events models""" """authentik events models"""
import time
from collections import Counter
from datetime import timedelta from datetime import timedelta
from difflib import get_close_matches from difflib import get_close_matches
from functools import lru_cache from functools import lru_cache
@ -11,11 +9,6 @@ from uuid import uuid4
from django.apps import apps from django.apps import apps
from django.db import connection, models from django.db import connection, models
from django.db.models import Count, ExpressionWrapper, F
from django.db.models.fields import DurationField
from django.db.models.functions import Extract
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
from django.http import HttpRequest from django.http import HttpRequest
from django.http.request import QueryDict from django.http.request import QueryDict
from django.utils.timezone import now from django.utils.timezone import now
@ -124,60 +117,6 @@ class EventAction(models.TextChoices):
CUSTOM_PREFIX = "custom_" CUSTOM_PREFIX = "custom_"
class EventQuerySet(QuerySet):
"""Custom events query set with helper functions"""
def get_events_per(
self,
time_since: timedelta,
extract: Extract,
data_points: int,
) -> list[dict[str, int]]:
"""Get event count by hour in the last day, fill with zeros"""
_now = now()
max_since = timedelta(days=60)
# Allow maximum of 60 days to limit load
if time_since.total_seconds() > max_since.total_seconds():
time_since = max_since
date_from = _now - time_since
result = (
self.filter(created__gte=date_from)
.annotate(age=ExpressionWrapper(_now - F("created"), output_field=DurationField()))
.annotate(age_interval=extract("age"))
.values("age_interval")
.annotate(count=Count("pk"))
.order_by("age_interval")
)
data = Counter({int(d["age_interval"]): d["count"] for d in result})
results = []
interval_delta = time_since / data_points
for interval in range(1, -data_points, -1):
results.append(
{
"x_cord": time.mktime((_now + (interval_delta * interval)).timetuple()) * 1000,
"y_cord": data[interval * -1],
}
)
return results
class EventManager(Manager):
"""Custom helper methods for Events"""
def get_queryset(self) -> QuerySet:
"""use custom queryset"""
return EventQuerySet(self.model, using=self._db)
def get_events_per(
self,
time_since: timedelta,
extract: Extract,
data_points: int,
) -> list[dict[str, int]]:
"""Wrap method from queryset"""
return self.get_queryset().get_events_per(time_since, extract, data_points)
class Event(SerializerModel, ExpiringModel): class Event(SerializerModel, ExpiringModel):
"""An individual Audit/Metrics/Notification/Error Event""" """An individual Audit/Metrics/Notification/Error Event"""
@ -193,8 +132,6 @@ class Event(SerializerModel, ExpiringModel):
# Shadow the expires attribute from ExpiringModel to override the default duration # Shadow the expires attribute from ExpiringModel to override the default duration
expires = models.DateTimeField(default=default_event_duration) expires = models.DateTimeField(default=default_event_duration)
objects = EventManager()
@staticmethod @staticmethod
def _get_app_from_request(request: HttpRequest) -> str: def _get_app_from_request(request: HttpRequest) -> str:
if not isinstance(request, HttpRequest): if not isinstance(request, HttpRequest):

View File

@ -37,6 +37,9 @@ class WebsocketMessageInstruction(IntEnum):
# Provider specific message # Provider specific message
PROVIDER_SPECIFIC = 3 PROVIDER_SPECIFIC = 3
# Session ended
SESSION_END = 4
@dataclass(slots=True) @dataclass(slots=True)
class WebsocketMessage: class WebsocketMessage:
@ -145,6 +148,14 @@ class OutpostConsumer(JsonWebsocketConsumer):
asdict(WebsocketMessage(instruction=WebsocketMessageInstruction.TRIGGER_UPDATE)) asdict(WebsocketMessage(instruction=WebsocketMessageInstruction.TRIGGER_UPDATE))
) )
def event_session_end(self, event):
"""Event handler which is called when a session is ended"""
self.send_json(
asdict(
WebsocketMessage(instruction=WebsocketMessageInstruction.SESSION_END, args=event)
)
)
def event_provider_specific(self, event): def event_provider_specific(self, event):
"""Event handler which can be called by provider-specific """Event handler which can be called by provider-specific
implementations to send specific messages to the outpost""" implementations to send specific messages to the outpost"""

View File

@ -1,17 +1,24 @@
"""authentik outpost signals""" """authentik outpost signals"""
from django.contrib.auth.signals import user_logged_out
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Model from django.db.models import Model
from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save from django.db.models.signals import m2m_changed, post_save, pre_delete, pre_save
from django.dispatch import receiver from django.dispatch import receiver
from django.http import HttpRequest
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.brands.models import Brand from authentik.brands.models import Brand
from authentik.core.models import Provider from authentik.core.models import AuthenticatedSession, Provider, User
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.utils.reflection import class_to_path from authentik.lib.utils.reflection import class_to_path
from authentik.outposts.models import Outpost, OutpostServiceConnection from authentik.outposts.models import Outpost, OutpostServiceConnection
from authentik.outposts.tasks import CACHE_KEY_OUTPOST_DOWN, outpost_controller, outpost_post_save from authentik.outposts.tasks import (
CACHE_KEY_OUTPOST_DOWN,
outpost_controller,
outpost_post_save,
outpost_session_end,
)
LOGGER = get_logger() LOGGER = get_logger()
UPDATE_TRIGGERING_MODELS = ( UPDATE_TRIGGERING_MODELS = (
@ -73,3 +80,17 @@ def pre_delete_cleanup(sender, instance: Outpost, **_):
instance.user.delete() instance.user.delete()
cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance) cache.set(CACHE_KEY_OUTPOST_DOWN % instance.pk.hex, instance)
outpost_controller.delay(instance.pk.hex, action="down", from_cache=True) outpost_controller.delay(instance.pk.hex, action="down", from_cache=True)
@receiver(user_logged_out)
def logout_revoke_direct(sender: type[User], request: HttpRequest, **_):
"""Catch logout by direct logout and forward to providers"""
if not request.session or not request.session.session_key:
return
outpost_session_end.delay(request.session.session_key)
@receiver(pre_delete, sender=AuthenticatedSession)
def logout_revoke(sender: type[AuthenticatedSession], instance: AuthenticatedSession, **_):
"""Catch logout by expiring sessions being deleted"""
outpost_session_end.delay(instance.session.session_key)

View File

@ -1,5 +1,6 @@
"""outpost tasks""" """outpost tasks"""
from hashlib import sha256
from os import R_OK, access from os import R_OK, access
from pathlib import Path from pathlib import Path
from socket import gethostname from socket import gethostname
@ -49,6 +50,11 @@ LOGGER = get_logger()
CACHE_KEY_OUTPOST_DOWN = "goauthentik.io/outposts/teardown/%s" CACHE_KEY_OUTPOST_DOWN = "goauthentik.io/outposts/teardown/%s"
def hash_session_key(session_key: str) -> str:
"""Hash the session key for sending session end signals"""
return sha256(session_key.encode("ascii")).hexdigest()
def controller_for_outpost(outpost: Outpost) -> type[BaseController] | None: def controller_for_outpost(outpost: Outpost) -> type[BaseController] | None:
"""Get a controller for the outpost, when a service connection is defined""" """Get a controller for the outpost, when a service connection is defined"""
if not outpost.service_connection: if not outpost.service_connection:
@ -289,3 +295,20 @@ def outpost_connection_discovery(self: SystemTask):
url=unix_socket_path, url=unix_socket_path,
) )
self.set_status(TaskStatus.SUCCESSFUL, *messages) self.set_status(TaskStatus.SUCCESSFUL, *messages)
@CELERY_APP.task()
def outpost_session_end(session_id: str):
"""Update outpost instances connected to a single outpost"""
layer = get_channel_layer()
hashed_session_id = hash_session_key(session_id)
for outpost in Outpost.objects.all():
LOGGER.info("Sending session end signal to outpost", outpost=outpost)
group = OUTPOST_GROUP % {"outpost_pk": str(outpost.pk)}
async_to_sync(layer.group_send)(
group,
{
"type": "event.session.end",
"session_id": hashed_session_id,
},
)

View File

@ -1,11 +1,9 @@
"""Websocket tests""" """Websocket tests"""
from dataclasses import asdict from dataclasses import asdict
from unittest.mock import patch
from channels.routing import URLRouter from channels.routing import URLRouter
from channels.testing import WebsocketCommunicator from channels.testing import WebsocketCommunicator
from django.contrib.contenttypes.models import ContentType
from django.test import TransactionTestCase from django.test import TransactionTestCase
from authentik import __version__ from authentik import __version__
@ -16,12 +14,6 @@ from authentik.providers.proxy.models import ProxyProvider
from authentik.root import websocket from authentik.root import websocket
def patched__get_ct_cached(app_label, codename):
"""Caches `ContentType` instances like its `QuerySet` does."""
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestOutpostWS(TransactionTestCase): class TestOutpostWS(TransactionTestCase):
"""Websocket tests""" """Websocket tests"""

View File

@ -387,8 +387,7 @@ class TestAuthorize(OAuthTestCase):
self.assertEqual( self.assertEqual(
response.url, response.url,
( (
f"http://localhost#access_token={token.token}" f"http://localhost#id_token={provider.encode(token.id_token.to_dict())}"
f"&id_token={provider.encode(token.id_token.to_dict())}"
f"&token_type={TOKEN_TYPE}" f"&token_type={TOKEN_TYPE}"
f"&expires_in={int(expires)}&state={state}" f"&expires_in={int(expires)}&state={state}"
), ),
@ -563,7 +562,6 @@ class TestAuthorize(OAuthTestCase):
"url": "http://localhost", "url": "http://localhost",
"title": f"Redirecting to {app.name}...", "title": f"Redirecting to {app.name}...",
"attrs": { "attrs": {
"access_token": token.token,
"id_token": provider.encode(token.id_token.to_dict()), "id_token": provider.encode(token.id_token.to_dict()),
"token_type": TOKEN_TYPE, "token_type": TOKEN_TYPE,
"expires_in": "3600", "expires_in": "3600",

View File

@ -150,12 +150,12 @@ class OAuthAuthorizationParams:
self.check_redirect_uri() self.check_redirect_uri()
self.check_grant() self.check_grant()
self.check_scope(github_compat) self.check_scope(github_compat)
self.check_nonce()
self.check_code_challenge()
if self.request: if self.request:
raise AuthorizeError( raise AuthorizeError(
self.redirect_uri, "request_not_supported", self.grant_type, self.state self.redirect_uri, "request_not_supported", self.grant_type, self.state
) )
self.check_nonce()
self.check_code_challenge()
def check_grant(self): def check_grant(self):
"""Check grant""" """Check grant"""
@ -630,7 +630,6 @@ class OAuthFulfillmentStage(StageView):
if self.params.response_type in [ if self.params.response_type in [
ResponseTypes.ID_TOKEN_TOKEN, ResponseTypes.ID_TOKEN_TOKEN,
ResponseTypes.CODE_ID_TOKEN_TOKEN, ResponseTypes.CODE_ID_TOKEN_TOKEN,
ResponseTypes.ID_TOKEN,
ResponseTypes.CODE_TOKEN, ResponseTypes.CODE_TOKEN,
]: ]:
query_fragment["access_token"] = token.token query_fragment["access_token"] = token.token

View File

@ -1,23 +0,0 @@
"""Proxy provider signals"""
from django.contrib.auth.signals import user_logged_out
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from django.http import HttpRequest
from authentik.core.models import AuthenticatedSession, User
from authentik.providers.proxy.tasks import proxy_on_logout
@receiver(user_logged_out)
def logout_proxy_revoke_direct(sender: type[User], request: HttpRequest, **_):
"""Catch logout by direct logout and forward to proxy providers"""
if not request.session or not request.session.session_key:
return
proxy_on_logout.delay(request.session.session_key)
@receiver(pre_delete, sender=AuthenticatedSession)
def logout_proxy_revoke(sender: type[AuthenticatedSession], instance: AuthenticatedSession, **_):
"""Catch logout by expiring sessions being deleted"""
proxy_on_logout.delay(instance.session.session_key)

View File

@ -1,26 +0,0 @@
"""proxy provider tasks"""
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from authentik.outposts.consumer import OUTPOST_GROUP
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.oauth2.id_token import hash_session_key
from authentik.root.celery import CELERY_APP
@CELERY_APP.task()
def proxy_on_logout(session_id: str):
"""Update outpost instances connected to a single outpost"""
layer = get_channel_layer()
hashed_session_id = hash_session_key(session_id)
for outpost in Outpost.objects.filter(type=OutpostType.PROXY):
group = OUTPOST_GROUP % {"outpost_pk": str(outpost.pk)}
async_to_sync(layer.group_send)(
group,
{
"type": "event.provider.specific",
"sub_type": "logout",
"session_id": hashed_session_id,
},
)

View File

@ -20,6 +20,9 @@ from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.engine import PolicyEngine from authentik.policies.engine import PolicyEngine
from authentik.policies.views import PolicyAccessView from authentik.policies.views import PolicyAccessView
from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider from authentik.providers.rac.models import ConnectionToken, Endpoint, RACProvider
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
PLAN_CONNECTION_SETTINGS = "connection_settings"
class RACStartView(PolicyAccessView): class RACStartView(PolicyAccessView):
@ -109,10 +112,15 @@ class RACFinalStage(RedirectStage):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_challenge(self, *args, **kwargs) -> RedirectChallenge: def get_challenge(self, *args, **kwargs) -> RedirectChallenge:
settings = self.executor.plan.context.get(PLAN_CONNECTION_SETTINGS)
if not settings:
settings = self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).get(
PLAN_CONNECTION_SETTINGS
)
token = ConnectionToken.objects.create( token = ConnectionToken.objects.create(
provider=self.provider, provider=self.provider,
endpoint=self.endpoint, endpoint=self.endpoint,
settings=self.executor.plan.context.get("connection_settings", {}), settings=settings or {},
session=self.request.session["authenticatedsession"], session=self.request.session["authenticatedsession"],
expires=now() + timedelta_from_string(self.provider.connection_expiry), expires=now() + timedelta_from_string(self.provider.connection_expiry),
expiring=True, expiring=True,

View File

@ -190,6 +190,7 @@ class SAMLProviderSerializer(ProviderSerializer):
"sign_response", "sign_response",
"sp_binding", "sp_binding",
"default_relay_state", "default_relay_state",
"default_name_id_policy",
"url_download_metadata", "url_download_metadata",
"url_sso_post", "url_sso_post",
"url_sso_redirect", "url_sso_redirect",

View File

@ -0,0 +1,31 @@
# Generated by Django 5.1.11 on 2025-06-18 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_saml", "0018_alter_samlprovider_acs_url"),
]
operations = [
migrations.AddField(
model_name="samlprovider",
name="default_name_id_policy",
field=models.TextField(
choices=[
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
(
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"Windows",
),
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
],
default="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
),
),
]

View File

@ -12,6 +12,7 @@ from authentik.core.models import PropertyMapping, Provider
from authentik.crypto.models import CertificateKeyPair from authentik.crypto.models import CertificateKeyPair
from authentik.lib.models import DomainlessURLValidator from authentik.lib.models import DomainlessURLValidator
from authentik.lib.utils.time import timedelta_string_validator from authentik.lib.utils.time import timedelta_string_validator
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
DSA_SHA1, DSA_SHA1,
ECDSA_SHA1, ECDSA_SHA1,
@ -179,6 +180,9 @@ class SAMLProvider(Provider):
default_relay_state = models.TextField( default_relay_state = models.TextField(
default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins") default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
) )
default_name_id_policy = models.TextField(
choices=SAMLNameIDPolicy.choices, default=SAMLNameIDPolicy.UNSPECIFIED
)
sign_assertion = models.BooleanField(default=True) sign_assertion = models.BooleanField(default=True)
sign_response = models.BooleanField(default=False) sign_response = models.BooleanField(default=False)

View File

@ -205,6 +205,13 @@ class AssertionProcessor:
def get_name_id(self) -> Element: def get_name_id(self) -> Element:
"""Get NameID Element""" """Get NameID Element"""
name_id = Element(f"{{{NS_SAML_ASSERTION}}}NameID") name_id = Element(f"{{{NS_SAML_ASSERTION}}}NameID")
# For requests that don't specify a NameIDPolicy, check if we
# can fall back to the provider default
if (
self.auth_n_request.name_id_policy == SAML_NAME_ID_FORMAT_UNSPECIFIED
and self.provider.default_name_id_policy != SAML_NAME_ID_FORMAT_UNSPECIFIED
):
self.auth_n_request.name_id_policy = self.provider.default_name_id_policy
name_id.attrib["Format"] = self.auth_n_request.name_id_policy name_id.attrib["Format"] = self.auth_n_request.name_id_policy
# persistent is used as a fallback, so always generate it # persistent is used as a fallback, so always generate it
persistent = self.http_request.user.uid persistent = self.http_request.user.uid

View File

@ -13,6 +13,7 @@ from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.exceptions import CannotHandleAssertion from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.utils.encoding import decode_base64_and_inflate from authentik.providers.saml.utils.encoding import decode_base64_and_inflate
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
DSA_SHA1, DSA_SHA1,
NS_MAP, NS_MAP,
@ -175,7 +176,9 @@ class AuthNRequestParser:
def idp_initiated(self) -> AuthNRequest: def idp_initiated(self) -> AuthNRequest:
"""Create IdP Initiated AuthNRequest""" """Create IdP Initiated AuthNRequest"""
relay_state = None request = AuthNRequest(relay_state=None)
if self.provider.default_relay_state != "": if self.provider.default_relay_state != "":
relay_state = self.provider.default_relay_state request.relay_state = self.provider.default_relay_state
return AuthNRequest(relay_state=relay_state) if self.provider.default_name_id_policy != SAMLNameIDPolicy.UNSPECIFIED:
request.name_id_policy = self.provider.default_name_id_policy
return request

View File

@ -13,6 +13,7 @@ from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.utils.encoding import PEM_FOOTER, PEM_HEADER from authentik.providers.saml.utils.encoding import PEM_FOOTER, PEM_HEADER
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ( from authentik.sources.saml.processors.constants import (
NS_MAP, NS_MAP,
NS_SAML_METADATA, NS_SAML_METADATA,
@ -46,6 +47,7 @@ class ServiceProviderMetadata:
auth_n_request_signed: bool auth_n_request_signed: bool
assertion_signed: bool assertion_signed: bool
name_id_policy: SAMLNameIDPolicy
signing_keypair: CertificateKeyPair | None = None signing_keypair: CertificateKeyPair | None = None
@ -60,6 +62,7 @@ class ServiceProviderMetadata:
provider.issuer = self.entity_id provider.issuer = self.entity_id
provider.sp_binding = self.acs_binding provider.sp_binding = self.acs_binding
provider.acs_url = self.acs_location provider.acs_url = self.acs_location
provider.default_name_id_policy = self.name_id_policy
if self.signing_keypair and self.auth_n_request_signed: if self.signing_keypair and self.auth_n_request_signed:
self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate" self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate"
self.signing_keypair.save() self.signing_keypair.save()
@ -148,6 +151,11 @@ class ServiceProviderMetadataParser:
if signing_keypair: if signing_keypair:
self.check_signature(root, signing_keypair) self.check_signature(root, signing_keypair)
name_id_format = descriptor.findall(f"{{{NS_SAML_METADATA}}}NameIDFormat")
name_id_policy = SAMLNameIDPolicy.UNSPECIFIED
if len(name_id_format) > 0:
name_id_policy = SAMLNameIDPolicy(name_id_format[0].text)
return ServiceProviderMetadata( return ServiceProviderMetadata(
entity_id=entity_id, entity_id=entity_id,
acs_binding=acs_binding, acs_binding=acs_binding,
@ -155,4 +163,5 @@ class ServiceProviderMetadataParser:
auth_n_request_signed=auth_n_request_signed, auth_n_request_signed=auth_n_request_signed,
assertion_signed=assertion_signed, assertion_signed=assertion_signed,
signing_keypair=signing_keypair, signing_keypair=signing_keypair,
name_id_policy=name_id_policy,
) )

View File

@ -4,7 +4,7 @@
cacheDuration="PT604800S" cacheDuration="PT604800S"
entityID="http://localhost:8080/saml/metadata"> entityID="http://localhost:8080/saml/metadata">
<md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> <md:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/saml/acs" Location="http://localhost:8080/saml/acs"
index="1" /> index="1" />

View File

@ -14,6 +14,7 @@ from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.metadata import MetadataProcessor from authentik.providers.saml.processors.metadata import MetadataProcessor
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
from authentik.sources.saml.models import SAMLNameIDPolicy
from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA
@ -86,6 +87,7 @@ class TestServiceProviderMetadataParser(TestCase):
self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs") self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs")
self.assertEqual(provider.issuer, "http://localhost:8080/saml/metadata") self.assertEqual(provider.issuer, "http://localhost:8080/saml/metadata")
self.assertEqual(provider.sp_binding, SAMLBindings.POST) self.assertEqual(provider.sp_binding, SAMLBindings.POST)
self.assertEqual(provider.default_name_id_policy, SAMLNameIDPolicy.EMAIL)
self.assertEqual( self.assertEqual(
len(provider.property_mappings.all()), len(provider.property_mappings.all()),
len(SAMLPropertyMapping.objects.exclude(managed__isnull=True)), len(SAMLPropertyMapping.objects.exclude(managed__isnull=True)),

View File

@ -1,12 +1,29 @@
"""test decorators api""" """test decorators api"""
from django.urls import reverse
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from rest_framework.viewsets import ModelViewSet
from authentik.core.models import Application from authentik.core.models import Application
from authentik.core.tests.utils import create_test_user from authentik.core.tests.utils import create_test_user
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.rbac.decorators import permission_required
class MVS(ModelViewSet):
queryset = Application.objects.all()
lookup_field = "slug"
@permission_required("authentik_core.view_application", ["authentik_events.view_event"])
@action(detail=True, pagination_class=None, filter_backends=[])
def test(self, request: Request, slug: str):
self.get_object()
return Response(status=200)
class TestAPIDecorators(APITestCase): class TestAPIDecorators(APITestCase):
@ -18,41 +35,33 @@ class TestAPIDecorators(APITestCase):
def test_obj_perm_denied(self): def test_obj_perm_denied(self):
"""Test object perm denied""" """Test object perm denied"""
self.client.force_login(self.user) request = get_request("", user=self.user)
app = Application.objects.create(name=generate_id(), slug=generate_id()) app = Application.objects.create(name=generate_id(), slug=generate_id())
response = self.client.get( response = MVS.as_view({"get": "test"})(request, slug=app.slug)
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug})
)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
def test_obj_perm_global(self): def test_obj_perm_global(self):
"""Test object perm successful (global)""" """Test object perm successful (global)"""
assign_perm("authentik_core.view_application", self.user) assign_perm("authentik_core.view_application", self.user)
assign_perm("authentik_events.view_event", self.user) assign_perm("authentik_events.view_event", self.user)
self.client.force_login(self.user)
app = Application.objects.create(name=generate_id(), slug=generate_id()) app = Application.objects.create(name=generate_id(), slug=generate_id())
response = self.client.get( request = get_request("", user=self.user)
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug}) response = MVS.as_view({"get": "test"})(request, slug=app.slug)
) self.assertEqual(response.status_code, 200, response.data)
self.assertEqual(response.status_code, 200)
def test_obj_perm_scoped(self): def test_obj_perm_scoped(self):
"""Test object perm successful (scoped)""" """Test object perm successful (scoped)"""
assign_perm("authentik_events.view_event", self.user) assign_perm("authentik_events.view_event", self.user)
app = Application.objects.create(name=generate_id(), slug=generate_id()) app = Application.objects.create(name=generate_id(), slug=generate_id())
assign_perm("authentik_core.view_application", self.user, app) assign_perm("authentik_core.view_application", self.user, app)
self.client.force_login(self.user) request = get_request("", user=self.user)
response = self.client.get( response = MVS.as_view({"get": "test"})(request, slug=app.slug)
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug})
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
def test_other_perm_denied(self): def test_other_perm_denied(self):
"""Test other perm denied""" """Test other perm denied"""
self.client.force_login(self.user)
app = Application.objects.create(name=generate_id(), slug=generate_id()) app = Application.objects.create(name=generate_id(), slug=generate_id())
assign_perm("authentik_core.view_application", self.user, app) assign_perm("authentik_core.view_application", self.user, app)
response = self.client.get( request = get_request("", user=self.user)
reverse("authentik_api:application-metrics", kwargs={"slug": app.slug}) response = MVS.as_view({"get": "test"})(request, slug=app.slug)
)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)

View File

@ -166,6 +166,7 @@ SPECTACULAR_SETTINGS = {
"UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification", "UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification",
"UserTypeEnum": "authentik.core.models.UserTypes", "UserTypeEnum": "authentik.core.models.UserTypes",
"OutgoingSyncDeleteAction": "authentik.lib.sync.outgoing.models.OutgoingSyncDeleteAction", "OutgoingSyncDeleteAction": "authentik.lib.sync.outgoing.models.OutgoingSyncDeleteAction",
"SAMLNameIDPolicyEnum": "authentik.sources.saml.models.SAMLNameIDPolicy",
}, },
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False, "ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,
"ENUM_GENERATE_CHOICE_DESCRIPTION": False, "ENUM_GENERATE_CHOICE_DESCRIPTION": False,

View File

@ -3,25 +3,44 @@
import os import os
from argparse import ArgumentParser from argparse import ArgumentParser
from unittest import TestCase from unittest import TestCase
from unittest.mock import patch
import pytest import pytest
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.test.runner import DiscoverRunner from django.test.runner import DiscoverRunner
from structlog.stdlib import get_logger
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.sentry import sentry_init from authentik.lib.sentry import sentry_init
from authentik.root.signals import post_startup, pre_startup, startup from authentik.root.signals import post_startup, pre_startup, startup
from tests.e2e.utils import get_docker_tag
# globally set maxDiff to none to show full assert error # globally set maxDiff to none to show full assert error
TestCase.maxDiff = None TestCase.maxDiff = None
def get_docker_tag() -> str:
"""Get docker-tag based off of CI variables"""
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
branch_name = os.environ.get(default_branch, "main")
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
return f"gh-{branch_name}"
def patched__get_ct_cached(app_label, codename):
"""Caches `ContentType` instances like its `QuerySet` does."""
return ContentType.objects.get(app_label=app_label, permission__codename=codename)
class PytestTestRunner(DiscoverRunner): # pragma: no cover class PytestTestRunner(DiscoverRunner): # pragma: no cover
"""Runs pytest to discover and run tests.""" """Runs pytest to discover and run tests."""
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
self.logger = get_logger().bind(runner="pytest")
self.args = [] self.args = []
if self.failfast: if self.failfast:
@ -34,22 +53,33 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
if kwargs.get("no_capture", False): if kwargs.get("no_capture", False):
self.args.append("--capture=no") self.args.append("--capture=no")
self._setup_test_environment()
def _setup_test_environment(self):
"""Configure test environment settings"""
settings.TEST = True settings.TEST = True
settings.CELERY["task_always_eager"] = True settings.CELERY["task_always_eager"] = True
CONFIG.set("events.context_processors.geoip", "tests/GeoLite2-City-Test.mmdb")
CONFIG.set("events.context_processors.asn", "tests/GeoLite2-ASN-Test.mmdb")
CONFIG.set("blueprints_dir", "./blueprints")
CONFIG.set(
"outposts.container_image_base",
f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}",
)
CONFIG.set("tenants.enabled", False)
CONFIG.set("outposts.disable_embedded_outpost", False)
CONFIG.set("error_reporting.sample_rate", 0)
CONFIG.set("error_reporting.environment", "testing")
CONFIG.set("error_reporting.send_pii", True)
sentry_init()
# Test-specific configuration
test_config = {
"events.context_processors.geoip": "tests/GeoLite2-City-Test.mmdb",
"events.context_processors.asn": "tests/GeoLite2-ASN-Test.mmdb",
"blueprints_dir": "./blueprints",
"outposts.container_image_base": f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}",
"tenants.enabled": False,
"outposts.disable_embedded_outpost": False,
"error_reporting.sample_rate": 0,
"error_reporting.environment": "testing",
"error_reporting.send_pii": True,
}
for key, value in test_config.items():
CONFIG.set(key, value)
sentry_init()
self.logger.debug("Test environment configured")
# Send startup signals
pre_startup.send(sender=self, mode="test") pre_startup.send(sender=self, mode="test")
startup.send(sender=self, mode="test") startup.send(sender=self, mode="test")
post_startup.send(sender=self, mode="test") post_startup.send(sender=self, mode="test")
@ -72,7 +102,21 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
help="Disable any capturing of stdout/stderr during tests.", help="Disable any capturing of stdout/stderr during tests.",
) )
def run_tests(self, test_labels, extra_tests=None, **kwargs): def _validate_test_label(self, label: str) -> bool:
"""Validate test label format"""
if not label:
return False
# Check for invalid characters, but allow forward slashes and colons
# for paths and pytest markers
invalid_chars = set('\\*?"<>|')
if any(c in label for c in invalid_chars):
self.logger.error("Invalid characters in test label", label=label)
return False
return True
def run_tests(self, test_labels: list[str], extra_tests=None, **kwargs):
"""Run pytest and return the exitcode. """Run pytest and return the exitcode.
It translates some of Django's test command option to pytest's. It translates some of Django's test command option to pytest's.
@ -82,10 +126,17 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
The extra_tests argument has been deprecated since Django 5.x The extra_tests argument has been deprecated since Django 5.x
It is kept for compatibility with PyCharm's Django test runner. It is kept for compatibility with PyCharm's Django test runner.
""" """
if not test_labels:
self.logger.error("No test files specified")
return 1
for label in test_labels: for label in test_labels:
if not self._validate_test_label(label):
return 1
valid_label_found = False valid_label_found = False
label_as_path = os.path.abspath(label) label_as_path = os.path.abspath(label)
# File path has been specified # File path has been specified
if os.path.exists(label_as_path): if os.path.exists(label_as_path):
self.args.append(label_as_path) self.args.append(label_as_path)
@ -93,24 +144,31 @@ class PytestTestRunner(DiscoverRunner): # pragma: no cover
elif "::" in label: elif "::" in label:
self.args.append(label) self.args.append(label)
valid_label_found = True valid_label_found = True
# Convert dotted module path to file_path::class::method
else: else:
# Check if the label is a dotted module path
path_pieces = label.split(".") path_pieces = label.split(".")
# Check whether only class or class and method are specified
for i in range(-1, -3, -1): for i in range(-1, -3, -1):
path = os.path.join(*path_pieces[:i]) + ".py" try:
label_as_path = os.path.abspath(path) path = os.path.join(*path_pieces[:i]) + ".py"
if os.path.exists(label_as_path): if os.path.exists(path):
path_method = label_as_path + "::" + "::".join(path_pieces[i:]) if i < -1:
self.args.append(path_method) path_method = path + "::" + "::".join(path_pieces[i:])
valid_label_found = True self.args.append(path_method)
break else:
self.args.append(path)
valid_label_found = True
break
except (TypeError, IndexError):
continue
if not valid_label_found: if not valid_label_found:
raise RuntimeError( self.logger.error("Test file not found", label=label)
f"One of the test labels: {label!r}, " return 1
f"is not supported. Use a dotted module name or "
f"path instead."
)
return pytest.main(self.args) self.logger.info("Running tests", test_files=self.args)
with patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached):
try:
return pytest.main(self.args)
except Exception as e:
self.logger.error("Error running tests", error=str(e), test_files=self.args)
return 1

View File

@ -0,0 +1,32 @@
# Generated by Django 5.1.11 on 2025-06-18 09:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_sources_saml", "0019_migrate_usersamlsourceconnection_identifier"),
]
operations = [
migrations.AlterField(
model_name="samlsource",
name="name_id_policy",
field=models.TextField(
choices=[
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", "Persistent"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", "X509"),
(
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
"Windows",
),
("urn:oasis:names:tc:SAML:2.0:nameid-format:transient", "Transient"),
("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "Unspecified"),
],
default="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
help_text="NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent.",
),
),
]

View File

@ -39,6 +39,7 @@ from authentik.sources.saml.processors.constants import (
SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT, SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_TRANSIENT, SAML_NAME_ID_FORMAT_TRANSIENT,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
SAML_NAME_ID_FORMAT_WINDOWS, SAML_NAME_ID_FORMAT_WINDOWS,
SAML_NAME_ID_FORMAT_X509, SAML_NAME_ID_FORMAT_X509,
SHA1, SHA1,
@ -73,6 +74,7 @@ class SAMLNameIDPolicy(models.TextChoices):
X509 = SAML_NAME_ID_FORMAT_X509 X509 = SAML_NAME_ID_FORMAT_X509
WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS
TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT
UNSPECIFIED = SAML_NAME_ID_FORMAT_UNSPECIFIED
class SAMLSource(Source): class SAMLSource(Source):

File diff suppressed because one or more lines are too long

View File

@ -100,9 +100,11 @@ def send_mail(
# Because we use the Message-ID as UID for the task, manually assign it # Because we use the Message-ID as UID for the task, manually assign it
message_object.extra_headers["Message-ID"] = message_id message_object.extra_headers["Message-ID"] = message_id
# Add the logo (we can't add it in the previous message since MIMEImage # Add the logo if it is used in the email body (we can't add it in the
# can't be converted to json) # previous message since MIMEImage can't be converted to json)
message_object.attach(logo_data()) body = get_email_body(message_object)
if "cid:logo" in body:
message_object.attach(logo_data())
if ( if (
message_object.to message_object.to

View File

@ -96,7 +96,7 @@
<table width="100%" style="background-color: #FFFFFF; border-spacing: 0; margin-top: 15px;"> <table width="100%" style="background-color: #FFFFFF; border-spacing: 0; margin-top: 15px;">
<tr height="80"> <tr height="80">
<td align="center" style="padding: 20px 0;"> <td align="center" style="padding: 20px 0;">
<img src="{% block logo_url %}cid:logo.png{% endblock %}" border="0=" alt="authentik logo" class="flexibleImage logo"> <img src="{% block logo_url %}cid:logo{% endblock %}" border="0=" alt="authentik logo" class="flexibleImage logo">
</td> </td>
</tr> </tr>
{% block content %} {% block content %}

View File

@ -19,7 +19,8 @@ def logo_data() -> MIMEImage:
path = Path("web/dist/assets/icons/icon_left_brand.png") path = Path("web/dist/assets/icons/icon_left_brand.png")
with open(path, "rb") as _logo_file: with open(path, "rb") as _logo_file:
logo = MIMEImage(_logo_file.read()) logo = MIMEImage(_logo_file.read())
logo.add_header("Content-ID", "logo.png") logo.add_header("Content-ID", "<logo>")
logo.add_header("Content-Disposition", "inline", filename="logo.png")
return logo return logo

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ services:
volumes: volumes:
- redis:/data - redis:/data
server: server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.2}
restart: unless-stopped restart: unless-stopped
command: server command: server
environment: environment:
@ -55,7 +55,7 @@ services:
redis: redis:
condition: service_healthy condition: service_healthy
worker: worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.0} image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.6.2}
restart: unless-stopped restart: unless-stopped
command: worker command: worker
environment: environment:

9
go.mod
View File

@ -4,6 +4,7 @@ go 1.24.0
require ( require (
beryju.io/ldap v0.1.0 beryju.io/ldap v0.1.0
github.com/avast/retry-go/v4 v4.6.1
github.com/coreos/go-oidc/v3 v3.14.1 github.com/coreos/go-oidc/v3 v3.14.1
github.com/getsentry/sentry-go v0.33.0 github.com/getsentry/sentry-go v0.33.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
@ -17,21 +18,21 @@ require (
github.com/gorilla/sessions v1.4.0 github.com/gorilla/sessions v1.4.0
github.com/gorilla/websocket v1.5.3 github.com/gorilla/websocket v1.5.3
github.com/grafana/pyroscope-go v1.2.2 github.com/grafana/pyroscope-go v1.2.2
github.com/jellydator/ttlcache/v3 v3.3.0 github.com/jellydator/ttlcache/v3 v3.4.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
github.com/pires/go-proxyproto v0.8.1 github.com/pires/go-proxyproto v0.8.1
github.com/prometheus/client_golang v1.22.0 github.com/prometheus/client_golang v1.22.0
github.com/redis/go-redis/v9 v9.9.0 github.com/redis/go-redis/v9 v9.10.0
github.com/sethvargo/go-envconfig v1.3.0 github.com/sethvargo/go-envconfig v1.3.0
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/wwt/guac v1.3.2 github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025060.1 goauthentik.io/api/v3 v3.2025062.1
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.14.0 golang.org/x/sync v0.15.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
) )

18
go.sum
View File

@ -41,6 +41,8 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7V
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@ -201,8 +203,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@ -249,8 +251,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM= github.com/redis/go-redis/v9 v9.10.0 h1:FxwK3eV8p/CQa0Ch276C7u2d0eNC9kCmAYQ7mCXCzVs=
github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/redis/go-redis/v9 v9.10.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
@ -296,8 +298,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
goauthentik.io/api/v3 v3.2025060.1 h1:H/TDuroJlQicuxrWEnLcO3lzQaHuR28xrUb1L2362Vo= goauthentik.io/api/v3 v3.2025062.1 h1:spvILDpDDWJNO3pM6QGqmryx6NvSchr1E8H60J/XUCA=
goauthentik.io/api/v3 v3.2025060.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw= goauthentik.io/api/v3 v3.2025062.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -382,8 +384,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

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

View File

@ -13,6 +13,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/avast/retry-go/v4"
"github.com/getsentry/sentry-go" "github.com/getsentry/sentry-go"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
@ -25,8 +26,6 @@ import (
"goauthentik.io/internal/utils/web" "goauthentik.io/internal/utils/web"
) )
type WSHandler func(ctx context.Context, args map[string]interface{})
const ConfigLogLevel = "log_level" const ConfigLogLevel = "log_level"
// APIController main controller which connects to the authentik api via http and ws // APIController main controller which connects to the authentik api via http and ws
@ -43,12 +42,11 @@ type APIController struct {
reloadOffset time.Duration reloadOffset time.Duration
wsConn *websocket.Conn eventConn *websocket.Conn
lastWsReconnect time.Time lastWsReconnect time.Time
wsIsReconnecting bool wsIsReconnecting bool
wsBackoffMultiplier int eventHandlers []EventHandler
wsHandlers []WSHandler refreshHandlers []func()
refreshHandlers []func()
instanceUUID uuid.UUID instanceUUID uuid.UUID
} }
@ -83,20 +81,19 @@ func NewAPIController(akURL url.URL, token string) *APIController {
// Because we don't know the outpost UUID, we simply do a list and pick the first // Because we don't know the outpost UUID, we simply do a list and pick the first
// The service account this token belongs to should only have access to a single outpost // The service account this token belongs to should only have access to a single outpost
var outposts *api.PaginatedOutpostList outposts, _ := retry.DoWithData[*api.PaginatedOutpostList](
var err error func() (*api.PaginatedOutpostList, error) {
for { outposts, _, err := apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
outposts, _, err = apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute() return outposts, err
},
if err == nil { retry.Attempts(0),
break retry.Delay(time.Second*3),
} retry.OnRetry(func(attempt uint, err error) {
log.WithError(err).Error("Failed to fetch outpost configuration, retrying in 3 seconds")
log.WithError(err).Error("Failed to fetch outpost configuration, retrying in 3 seconds") }),
time.Sleep(time.Second * 3) )
}
if len(outposts.Results) < 1 { if len(outposts.Results) < 1 {
panic("No outposts found with given token, ensure the given token corresponds to an authenitk Outpost") log.Panic("No outposts found with given token, ensure the given token corresponds to an authenitk Outpost")
} }
outpost := outposts.Results[0] outpost := outposts.Results[0]
@ -119,17 +116,16 @@ func NewAPIController(akURL url.URL, token string) *APIController {
token: token, token: token,
logger: log, logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(), instanceUUID: uuid.New(),
Outpost: outpost, Outpost: outpost,
wsHandlers: []WSHandler{}, eventHandlers: []EventHandler{},
wsBackoffMultiplier: 1, refreshHandlers: make([]func(), 0),
refreshHandlers: make([]func(), 0),
} }
ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset") ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset")
err = ac.initWS(akURL, outpost.Pk) err = ac.initEvent(akURL, outpost.Pk)
if err != nil { if err != nil {
go ac.reconnectWS() go ac.recentEvents()
} }
ac.configureRefreshSignal() ac.configureRefreshSignal()
return ac return ac
@ -200,7 +196,7 @@ func (a *APIController) OnRefresh() error {
return err return err
} }
func (a *APIController) getWebsocketPingArgs() map[string]interface{} { func (a *APIController) getEventPingArgs() map[string]interface{} {
args := map[string]interface{}{ args := map[string]interface{}{
"version": constants.VERSION, "version": constants.VERSION,
"buildHash": constants.BUILD(""), "buildHash": constants.BUILD(""),
@ -226,12 +222,12 @@ func (a *APIController) StartBackgroundTasks() error {
"build": constants.BUILD(""), "build": constants.BUILD(""),
}).Set(1) }).Set(1)
go func() { go func() {
a.logger.Debug("Starting WS Handler...") a.logger.Debug("Starting Event Handler...")
a.startWSHandler() a.startEventHandler()
}() }()
go func() { go func() {
a.logger.Debug("Starting WS Health notifier...") a.logger.Debug("Starting Event health notifier...")
a.startWSHealth() a.startEventHealth()
}() }()
go func() { go func() {
a.logger.Debug("Starting Interval updater...") a.logger.Debug("Starting Interval updater...")

View File

@ -11,6 +11,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/avast/retry-go/v4"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"goauthentik.io/internal/config" "goauthentik.io/internal/config"
@ -30,7 +31,7 @@ func (ac *APIController) getWebsocketURL(akURL url.URL, outpostUUID string, quer
return wsUrl return wsUrl
} }
func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error { func (ac *APIController) initEvent(akURL url.URL, outpostUUID string) error {
query := akURL.Query() query := akURL.Query()
query.Set("instance_uuid", ac.instanceUUID.String()) query.Set("instance_uuid", ac.instanceUUID.String())
@ -57,19 +58,19 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
return err return err
} }
ac.wsConn = ws ac.eventConn = ws
// Send hello message with our version // Send hello message with our version
msg := websocketMessage{ msg := Event{
Instruction: WebsocketInstructionHello, Instruction: EventKindHello,
Args: ac.getWebsocketPingArgs(), Args: ac.getEventPingArgs(),
} }
err = ws.WriteJSON(msg) err = ws.WriteJSON(msg)
if err != nil { if err != nil {
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik") ac.logger.WithField("logger", "authentik.outpost.events").WithError(err).Warning("Failed to hello to authentik")
return err return err
} }
ac.lastWsReconnect = time.Now() ac.lastWsReconnect = time.Now()
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID).Info("Successfully connected websocket") ac.logger.WithField("logger", "authentik.outpost.events").WithField("outpost", outpostUUID).Info("Successfully connected websocket")
return nil return nil
} }
@ -77,19 +78,19 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
func (ac *APIController) Shutdown() { func (ac *APIController) Shutdown() {
// Cleanly close the connection by sending a close message and then // Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection. // waiting (with timeout) for the server to close the connection.
err := ac.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) err := ac.eventConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil { if err != nil {
ac.logger.WithError(err).Warning("failed to write close message") ac.logger.WithError(err).Warning("failed to write close message")
return return
} }
err = ac.wsConn.Close() err = ac.eventConn.Close()
if err != nil { if err != nil {
ac.logger.WithError(err).Warning("failed to close websocket") ac.logger.WithError(err).Warning("failed to close websocket")
} }
ac.logger.Info("finished shutdown") ac.logger.Info("finished shutdown")
} }
func (ac *APIController) reconnectWS() { func (ac *APIController) recentEvents() {
if ac.wsIsReconnecting { if ac.wsIsReconnecting {
return return
} }
@ -100,46 +101,47 @@ func (ac *APIController) reconnectWS() {
Path: strings.ReplaceAll(ac.Client.GetConfig().Servers[0].URL, "api/v3", ""), Path: strings.ReplaceAll(ac.Client.GetConfig().Servers[0].URL, "api/v3", ""),
} }
attempt := 1 attempt := 1
for { _ = retry.Do(
q := u.Query() func() error {
q.Set("attempt", strconv.Itoa(attempt)) q := u.Query()
u.RawQuery = q.Encode() q.Set("attempt", strconv.Itoa(attempt))
err := ac.initWS(u, ac.Outpost.Pk) u.RawQuery = q.Encode()
attempt += 1 err := ac.initEvent(u, ac.Outpost.Pk)
if err != nil { attempt += 1
ac.logger.Infof("waiting %d seconds to reconnect", ac.wsBackoffMultiplier) if err != nil {
time.Sleep(time.Duration(ac.wsBackoffMultiplier) * time.Second) return err
ac.wsBackoffMultiplier = ac.wsBackoffMultiplier * 2
// Limit to 300 seconds (5m)
if ac.wsBackoffMultiplier >= 300 {
ac.wsBackoffMultiplier = 300
} }
} else {
ac.wsIsReconnecting = false ac.wsIsReconnecting = false
ac.wsBackoffMultiplier = 1 return nil
return },
} retry.Delay(1*time.Second),
} retry.MaxDelay(5*time.Minute),
retry.DelayType(retry.BackOffDelay),
retry.Attempts(0),
retry.OnRetry(func(attempt uint, err error) {
ac.logger.Infof("waiting %d seconds to reconnect", attempt)
}),
)
} }
func (ac *APIController) startWSHandler() { func (ac *APIController) startEventHandler() {
logger := ac.logger.WithField("loop", "ws-handler") logger := ac.logger.WithField("loop", "event-handler")
for { for {
var wsMsg websocketMessage var wsMsg Event
if ac.wsConn == nil { if ac.eventConn == nil {
go ac.reconnectWS() go ac.recentEvents()
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} }
err := ac.wsConn.ReadJSON(&wsMsg) err := ac.eventConn.ReadJSON(&wsMsg)
if err != nil { if err != nil {
ConnectionStatus.With(prometheus.Labels{ ConnectionStatus.With(prometheus.Labels{
"outpost_name": ac.Outpost.Name, "outpost_name": ac.Outpost.Name,
"outpost_type": ac.Server.Type(), "outpost_type": ac.Server.Type(),
"uuid": ac.instanceUUID.String(), "uuid": ac.instanceUUID.String(),
}).Set(0) }).Set(0)
logger.WithError(err).Warning("ws read error") logger.WithError(err).Warning("event read error")
go ac.reconnectWS() go ac.recentEvents()
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} }
@ -149,7 +151,8 @@ func (ac *APIController) startWSHandler() {
"uuid": ac.instanceUUID.String(), "uuid": ac.instanceUUID.String(),
}).Set(1) }).Set(1)
switch wsMsg.Instruction { switch wsMsg.Instruction {
case WebsocketInstructionTriggerUpdate: case EventKindAck:
case EventKindTriggerUpdate:
time.Sleep(ac.reloadOffset) time.Sleep(ac.reloadOffset)
logger.Debug("Got update trigger...") logger.Debug("Got update trigger...")
err := ac.OnRefresh() err := ac.OnRefresh()
@ -164,30 +167,33 @@ func (ac *APIController) startWSHandler() {
"build": constants.BUILD(""), "build": constants.BUILD(""),
}).SetToCurrentTime() }).SetToCurrentTime()
} }
case WebsocketInstructionProviderSpecific: default:
for _, h := range ac.wsHandlers { for _, h := range ac.eventHandlers {
h(context.Background(), wsMsg.Args) err := h(context.Background(), wsMsg)
if err != nil {
ac.logger.WithError(err).Warning("failed to run event handler")
}
} }
} }
} }
} }
func (ac *APIController) startWSHealth() { func (ac *APIController) startEventHealth() {
ticker := time.NewTicker(time.Second * 10) ticker := time.NewTicker(time.Second * 10)
for ; true; <-ticker.C { for ; true; <-ticker.C {
if ac.wsConn == nil { if ac.eventConn == nil {
go ac.reconnectWS() go ac.recentEvents()
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} }
err := ac.SendWSHello(map[string]interface{}{}) err := ac.SendEventHello(map[string]interface{}{})
if err != nil { if err != nil {
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error") ac.logger.WithField("loop", "event-health").WithError(err).Warning("event write error")
go ac.reconnectWS() go ac.recentEvents()
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
continue continue
} else { } else {
ac.logger.WithField("loop", "ws-health").Trace("hello'd") ac.logger.WithField("loop", "event-health").Trace("hello'd")
ConnectionStatus.With(prometheus.Labels{ ConnectionStatus.With(prometheus.Labels{
"outpost_name": ac.Outpost.Name, "outpost_name": ac.Outpost.Name,
"outpost_type": ac.Server.Type(), "outpost_type": ac.Server.Type(),
@ -230,19 +236,19 @@ func (ac *APIController) startIntervalUpdater() {
} }
} }
func (a *APIController) AddWSHandler(handler WSHandler) { func (a *APIController) AddEventHandler(handler EventHandler) {
a.wsHandlers = append(a.wsHandlers, handler) a.eventHandlers = append(a.eventHandlers, handler)
} }
func (a *APIController) SendWSHello(args map[string]interface{}) error { func (a *APIController) SendEventHello(args map[string]interface{}) error {
allArgs := a.getWebsocketPingArgs() allArgs := a.getEventPingArgs()
for key, value := range args { for key, value := range args {
allArgs[key] = value allArgs[key] = value
} }
aliveMsg := websocketMessage{ aliveMsg := Event{
Instruction: WebsocketInstructionHello, Instruction: EventKindHello,
Args: allArgs, Args: allArgs,
} }
err := a.wsConn.WriteJSON(aliveMsg) err := a.eventConn.WriteJSON(aliveMsg)
return err return err
} }

View File

@ -0,0 +1,37 @@
package ak
import (
"context"
"github.com/mitchellh/mapstructure"
)
type EventKind int
const (
// Code used to acknowledge a previous message
EventKindAck EventKind = 0
// Code used to send a healthcheck keepalive
EventKindHello EventKind = 1
// Code received to trigger a config update
EventKindTriggerUpdate EventKind = 2
// Code received to trigger some provider specific function
EventKindProviderSpecific EventKind = 3
// Code received to identify the end of a session
EventKindSessionEnd EventKind = 4
)
type EventHandler func(ctx context.Context, msg Event) error
type Event struct {
Instruction EventKind `json:"instruction"`
Args interface{} `json:"args"`
}
func (wm Event) ArgsAs(out interface{}) error {
return mapstructure.Decode(wm.Args, out)
}
type EventArgsSessionEnd struct {
SessionID string `mapstructure:"session_id"`
}

View File

@ -15,7 +15,7 @@ func URLMustParse(u string) *url.URL {
return ur return ur
} }
func TestWebsocketURL(t *testing.T) { func TestEventWebsocketURL(t *testing.T) {
u := URLMustParse("http://localhost:9000?foo=bar") u := URLMustParse("http://localhost:9000?foo=bar")
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77" uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
ac := &APIController{} ac := &APIController{}
@ -23,7 +23,7 @@ func TestWebsocketURL(t *testing.T) {
assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?foo=bar", nu.String()) assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?foo=bar", nu.String())
} }
func TestWebsocketURL_Query(t *testing.T) { func TestEventWebsocketURL_Query(t *testing.T) {
u := URLMustParse("http://localhost:9000?foo=bar") u := URLMustParse("http://localhost:9000?foo=bar")
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77" uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
ac := &APIController{} ac := &APIController{}
@ -33,7 +33,7 @@ func TestWebsocketURL_Query(t *testing.T) {
assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?bar=baz&foo=bar", nu.String()) assert.Equal(t, "ws://localhost:9000/ws/outpost/23470845-7263-4fe3-bd79-ec1d7bf77d77/?bar=baz&foo=bar", nu.String())
} }
func TestWebsocketURL_Subpath(t *testing.T) { func TestEventWebsocketURL_Subpath(t *testing.T) {
u := URLMustParse("http://localhost:9000/foo/bar/") u := URLMustParse("http://localhost:9000/foo/bar/")
uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77" uuid := "23470845-7263-4fe3-bd79-ec1d7bf77d77"
ac := &APIController{} ac := &APIController{}

View File

@ -1,19 +0,0 @@
package ak
type websocketInstruction int
const (
// WebsocketInstructionAck Code used to acknowledge a previous message
WebsocketInstructionAck websocketInstruction = 0
// WebsocketInstructionHello Code used to send a healthcheck keepalive
WebsocketInstructionHello websocketInstruction = 1
// WebsocketInstructionTriggerUpdate Code received to trigger a config update
WebsocketInstructionTriggerUpdate websocketInstruction = 2
// WebsocketInstructionProviderSpecific Code received to trigger some provider specific function
WebsocketInstructionProviderSpecific websocketInstruction = 3
)
type websocketMessage struct {
Instruction websocketInstruction `json:"instruction"`
Args map[string]interface{} `json:"args"`
}

View File

@ -55,11 +55,10 @@ func MockAK(outpost api.Outpost, globalConfig api.Config) *APIController {
token: token, token: token,
logger: log, logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(), instanceUUID: uuid.New(),
Outpost: outpost, Outpost: outpost,
wsBackoffMultiplier: 1, refreshHandlers: make([]func(), 0),
refreshHandlers: make([]func(), 0),
} }
ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset") ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset")
return ac return ac

View File

@ -127,7 +127,7 @@ func (fe *FlowExecutor) getAnswer(stage StageComponent) string {
return "" return ""
} }
func (fe *FlowExecutor) GetSession() *http.Cookie { func (fe *FlowExecutor) SessionCookie() *http.Cookie {
return fe.session return fe.session
} }

View File

@ -0,0 +1,19 @@
package flow
import "github.com/golang-jwt/jwt/v5"
type SessionCookieClaims struct {
jwt.Claims
SessionID string `json:"sid"`
Authenticated bool `json:"authenticated"`
}
func (fe *FlowExecutor) Session() *jwt.Token {
sc := fe.SessionCookie()
if sc == nil {
return nil
}
t, _, _ := jwt.NewParser().ParseUnverified(sc.Value, &SessionCookieClaims{})
return t
}

View File

@ -38,7 +38,14 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
username, err := instance.binder.GetUsername(bindDN) username, err := instance.binder.GetUsername(bindDN)
if err == nil { if err == nil {
selectedApp = instance.GetAppSlug() selectedApp = instance.GetAppSlug()
return instance.binder.Bind(username, req) c, err := instance.binder.Bind(username, req)
if c == ldap.LDAPResultSuccess {
f := instance.GetFlags(req.BindDN)
ls.connectionsSync.Lock()
ls.connections[f.SessionID()] = conn
ls.connectionsSync.Unlock()
}
return c, err
} else { } else {
req.Log().WithError(err).Debug("Username not for instance") req.Log().WithError(err).Debug("Username not for instance")
} }

View File

@ -27,8 +27,9 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
passed, err := fe.Execute() passed, err := fe.Execute()
flags := flags.UserFlags{ flags := flags.UserFlags{
Session: fe.GetSession(), Session: fe.SessionCookie(),
UserPk: flags.InvalidUserPK, SessionJWT: fe.Session(),
UserPk: flags.InvalidUserPK,
} }
// only set flags if we don't have flags for this DN yet // only set flags if we don't have flags for this DN yet
// as flags are only checked during the bind, we can remember whether a certain DN // as flags are only checked during the bind, we can remember whether a certain DN

View File

@ -0,0 +1,20 @@
package ldap
import "net"
func (ls *LDAPServer) Close(dn string, conn net.Conn) error {
ls.connectionsSync.Lock()
defer ls.connectionsSync.Unlock()
key := ""
for k, c := range ls.connections {
if c == conn {
key = k
break
}
}
if key == "" {
return nil
}
delete(ls.connections, key)
return nil
}

View File

@ -1,16 +1,30 @@
package flags package flags
import ( import (
"crypto/sha256"
"encoding/hex"
"net/http" "net/http"
"github.com/golang-jwt/jwt/v5"
"goauthentik.io/api/v3" "goauthentik.io/api/v3"
"goauthentik.io/internal/outpost/flow"
) )
const InvalidUserPK = -1 const InvalidUserPK = -1
type UserFlags struct { type UserFlags struct {
UserInfo *api.User UserInfo *api.User
UserPk int32 UserPk int32
CanSearch bool CanSearch bool
Session *http.Cookie Session *http.Cookie
SessionJWT *jwt.Token
}
func (uf UserFlags) SessionID() string {
if uf.SessionJWT == nil {
return ""
}
h := sha256.New()
h.Write([]byte(uf.SessionJWT.Claims.(*flow.SessionCookieClaims).SessionID))
return hex.EncodeToString(h.Sum(nil))
} }

View File

@ -18,21 +18,26 @@ import (
) )
type LDAPServer struct { type LDAPServer struct {
s *ldap.Server s *ldap.Server
log *log.Entry log *log.Entry
ac *ak.APIController ac *ak.APIController
cs *ak.CryptoStore cs *ak.CryptoStore
defaultCert *tls.Certificate defaultCert *tls.Certificate
providers []*ProviderInstance providers []*ProviderInstance
connections map[string]net.Conn
connectionsSync sync.Mutex
} }
func NewServer(ac *ak.APIController) ak.Outpost { func NewServer(ac *ak.APIController) ak.Outpost {
ls := &LDAPServer{ ls := &LDAPServer{
log: log.WithField("logger", "authentik.outpost.ldap"), log: log.WithField("logger", "authentik.outpost.ldap"),
ac: ac, ac: ac,
cs: ak.NewCryptoStore(ac.Client.CryptoApi), cs: ak.NewCryptoStore(ac.Client.CryptoApi),
providers: []*ProviderInstance{}, providers: []*ProviderInstance{},
connections: map[string]net.Conn{},
connectionsSync: sync.Mutex{},
} }
ac.AddEventHandler(ls.handleWSSessionEnd)
s := ldap.NewServer() s := ldap.NewServer()
s.EnforceLDAP = true s.EnforceLDAP = true
@ -50,6 +55,7 @@ func NewServer(ac *ak.APIController) ak.Outpost {
s.BindFunc("", ls) s.BindFunc("", ls)
s.UnbindFunc("", ls) s.UnbindFunc("", ls)
s.SearchFunc("", ls) s.SearchFunc("", ls)
s.CloseFunc("", ls)
return ls return ls
} }
@ -117,3 +123,23 @@ func (ls *LDAPServer) TimerFlowCacheExpiry(ctx context.Context) {
p.binder.TimerFlowCacheExpiry(ctx) p.binder.TimerFlowCacheExpiry(ctx)
} }
} }
func (ls *LDAPServer) handleWSSessionEnd(ctx context.Context, msg ak.Event) error {
if msg.Instruction != ak.EventKindSessionEnd {
return nil
}
mmsg := ak.EventArgsSessionEnd{}
err := msg.ArgsAs(&mmsg)
if err != nil {
return err
}
ls.connectionsSync.Lock()
defer ls.connectionsSync.Unlock()
ls.log.Info("Disconnecting session due to session end event")
conn, ok := ls.connections[mmsg.SessionID]
if !ok {
return nil
}
delete(ls.connections, mmsg.SessionID)
return conn.Close()
}

View File

@ -44,38 +44,40 @@ func (ds *DirectSearcher) SearchSubschema(req *search.Request) (ldap.ServerSearc
{ {
Name: "attributeTypes", Name: "attributeTypes",
Values: []string{ Values: []string{
"( 2.5.4.0 NAME 'objectClass' SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' NO-USER-MODIFICATION )",
"( 2.5.4.4 NAME 'sn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.3 NAME 'cn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.6 NAME 'c' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.7 NAME 'l' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.10 NAME 'o' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.11 NAME 'ou' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.12 NAME 'title' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.13 NAME 'description' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.20 NAME 'telephoneNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.31 NAME 'member' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )",
"( 2.5.4.42 NAME 'givenName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.21.2 NAME 'dITContentRules' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.5 NAME 'attributeTypes' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.6 NAME 'objectClasses' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 0.9.2342.19200300.100.1.1 NAME 'uid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 0.9.2342.19200300.100.1.1 NAME 'uid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.3 NAME 'mail' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 0.9.2342.19200300.100.1.3 NAME 'mail' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 0.9.2342.19200300.100.1.41 NAME 'mobile' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 0.9.2342.19200300.100.1.41 NAME 'mobile' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.13 NAME 'displayName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.146 NAME 'company' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.102 NAME 'memberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' NO-USER-MODIFICATION )", "( 1.2.840.113556.1.2.102 NAME 'memberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.2.13 NAME 'displayName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.131 NAME 'co' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 1.2.840.113556.1.2.131 NAME 'co' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.141 NAME 'department' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 1.2.840.113556.1.2.141 NAME 'department' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.2.146 NAME 'company' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.1 NAME 'name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE NO-USER-MODIFICATION )", "( 1.2.840.113556.1.4.1 NAME 'name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE NO-USER-MODIFICATION )",
"( 1.2.840.113556.1.4.44 NAME 'homeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 1.2.840.113556.1.4.221 NAME 'sAMAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.261 NAME 'division' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )", "( 1.2.840.113556.1.4.261 NAME 'division' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.44 NAME 'homeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.750 NAME 'groupType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )", "( 1.2.840.113556.1.4.750 NAME 'groupType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.2.840.113556.1.4.782 NAME 'objectCategory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )", "( 1.2.840.113556.1.4.782 NAME 'objectCategory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.0 NAME 'uidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )", "( 1.3.6.1.1.1.1.0 NAME 'uidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.1 NAME 'gidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )", "( 1.3.6.1.1.1.1.1 NAME 'gidNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )",
"( 1.3.6.1.1.1.1.12 NAME 'memberUid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )", "( 1.3.6.1.1.1.1.12 NAME 'memberUid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )",
"( 2.5.18.1 NAME 'createTimestamp' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODIFICATION )",
"( 2.5.18.2 NAME 'modifyTimestamp' SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE NO-USER-MODIFICATION )",
"( 2.5.21.2 NAME 'dITContentRules' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.5 NAME 'attributeTypes' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.21.6 NAME 'objectClasses' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' NO-USER-MODIFICATION )",
"( 2.5.4.0 NAME 'objectClass' SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' NO-USER-MODIFICATION )",
"( 2.5.4.10 NAME 'o' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.11 NAME 'ou' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.12 NAME 'title' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.13 NAME 'description' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )",
"( 2.5.4.20 NAME 'telephoneNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.3 NAME 'cn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.31 NAME 'member' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )",
"( 2.5.4.4 NAME 'sn' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.42 NAME 'givenName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.6 NAME 'c' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
"( 2.5.4.7 NAME 'l' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )",
// Custom attributes // Custom attributes
// Temporarily use 1.3.6.1.4.1.26027.1.1 as a base // Temporarily use 1.3.6.1.4.1.26027.1.1 as a base

View File

@ -53,6 +53,14 @@ func NewRequest(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (*Re
if err != nil && len(searchReq.Filter) > 0 { if err != nil && len(searchReq.Filter) > 0 {
l.WithError(err).WithField("objectClass", filterOC).Warning("invalid filter object class") l.WithError(err).WithField("objectClass", filterOC).Warning("invalid filter object class")
} }
// Handle comma-separated attributes
normalizedAttributes := normalizeAttributes(searchReq.Attributes)
if len(normalizedAttributes) != len(searchReq.Attributes) {
// Create a copy of the search request with normalized attributes
searchReq.Attributes = normalizedAttributes
}
return &Request{ return &Request{
SearchRequest: searchReq, SearchRequest: searchReq,
BindDN: bindDN, BindDN: bindDN,
@ -64,6 +72,31 @@ func NewRequest(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (*Re
}, span }, span
} }
// normalizeAttributes handles the case where attributes might be passed as comma-separated strings
// rather than as individual array elements
func normalizeAttributes(attributes []string) []string {
if len(attributes) == 0 {
return attributes
}
result := make([]string, 0, len(attributes))
for _, attr := range attributes {
if strings.Contains(attr, ",") {
// Split comma-separated attributes and add them individually
parts := strings.Split(attr, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" {
result = append(result, part)
}
}
} else {
result = append(result, attr)
}
}
return result
}
func (r *Request) Context() context.Context { func (r *Request) Context() context.Context {
return r.ctx return r.ctx
} }

View File

@ -0,0 +1,98 @@
package search
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNormalizeAttributes(t *testing.T) {
tests := []struct {
name string
input []string
expectedOutput []string
}{
{
name: "Empty input",
input: []string{},
expectedOutput: []string{},
},
{
name: "No commas",
input: []string{"uid", "cn", "sn"},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "Single comma-separated string",
input: []string{"uid,cn,sn"},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "Mixed input",
input: []string{"uid,cn", "sn"},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "With spaces",
input: []string{"uid, cn, sn"},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "Empty parts",
input: []string{"uid,, cn"},
expectedOutput: []string{"uid", "cn"},
},
{
name: "Single element",
input: []string{"uid"},
expectedOutput: []string{"uid"},
},
{
name: "Only commas",
input: []string{",,,"},
expectedOutput: []string{},
},
{
name: "Multiple comma-separated attributes",
input: []string{"uid,cn", "sn,mail", "givenName"},
expectedOutput: []string{"uid", "cn", "sn", "mail", "givenName"},
},
{
name: "Case preservation",
input: []string{"uid,CN,sAMAccountName"},
expectedOutput: []string{"uid", "CN", "sAMAccountName"},
},
{
name: "Leading and trailing spaces",
input: []string{" uid , cn , sn "},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "Real-world LDAP attribute examples",
input: []string{"objectClass,memberOf,mail", "sAMAccountName,userPrincipalName"},
expectedOutput: []string{"objectClass", "memberOf", "mail", "sAMAccountName", "userPrincipalName"},
},
{
name: "Jira-style attribute format",
input: []string{"uid,cn,sn"},
expectedOutput: []string{"uid", "cn", "sn"},
},
{
name: "Single string with single attribute",
input: []string{"cn"},
expectedOutput: []string{"cn"},
},
{
name: "Mix of standard and operational attributes",
input: []string{"uid,+", "createTimestamp"},
expectedOutput: []string{"uid", "+", "createTimestamp"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := normalizeAttributes(tt.input)
assert.Equal(t, tt.expectedOutput, result)
})
}
}

View File

@ -5,6 +5,7 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"encoding/gob" "encoding/gob"
"encoding/hex"
"fmt" "fmt"
"html/template" "html/template"
"net/http" "net/http"
@ -118,8 +119,8 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
mux := mux.NewRouter() mux := mux.NewRouter()
// Save cookie name, based on hashed client ID // Save cookie name, based on hashed client ID
h := sha256.New() hs := sha256.Sum256([]byte(*p.ClientId))
bs := string(h.Sum([]byte(*p.ClientId))) bs := hex.EncodeToString(hs[:])
sessionName := fmt.Sprintf("authentik_proxy_%s", bs[:8]) sessionName := fmt.Sprintf("authentik_proxy_%s", bs[:8])
// When HOST_BROWSER is set, use that as Host header for token requests to make the issuer match // When HOST_BROWSER is set, use that as Host header for token requests to make the issuer match

View File

@ -66,7 +66,7 @@ func NewProxyServer(ac *ak.APIController) ak.Outpost {
globalMux.PathPrefix("/outpost.goauthentik.io/static").HandlerFunc(s.HandleStatic) globalMux.PathPrefix("/outpost.goauthentik.io/static").HandlerFunc(s.HandleStatic)
globalMux.Path("/outpost.goauthentik.io/ping").HandlerFunc(sentryutils.SentryNoSample(s.HandlePing)) globalMux.Path("/outpost.goauthentik.io/ping").HandlerFunc(sentryutils.SentryNoSample(s.HandlePing))
rootMux.PathPrefix("/").HandlerFunc(s.Handle) rootMux.PathPrefix("/").HandlerFunc(s.Handle)
ac.AddWSHandler(s.handleWSMessage) ac.AddEventHandler(s.handleWSMessage)
return s return s
} }

View File

@ -3,48 +3,27 @@ package proxyv2
import ( import (
"context" "context"
"github.com/mitchellh/mapstructure" "goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2/application" "goauthentik.io/internal/outpost/proxyv2/application"
) )
type WSProviderSubType string func (ps *ProxyServer) handleWSMessage(ctx context.Context, msg ak.Event) error {
if msg.Instruction != ak.EventKindSessionEnd {
const ( return nil
WSProviderSubTypeLogout WSProviderSubType = "logout"
)
type WSProviderMsg struct {
SubType WSProviderSubType `mapstructure:"sub_type"`
SessionID string `mapstructure:"session_id"`
}
func ParseWSProvider(args map[string]interface{}) (*WSProviderMsg, error) {
msg := &WSProviderMsg{}
err := mapstructure.Decode(args, &msg)
if err != nil {
return nil, err
} }
return msg, nil mmsg := ak.EventArgsSessionEnd{}
} err := msg.ArgsAs(&mmsg)
func (ps *ProxyServer) handleWSMessage(ctx context.Context, args map[string]interface{}) {
msg, err := ParseWSProvider(args)
if err != nil { if err != nil {
ps.log.WithError(err).Warning("invalid provider-specific ws message") return err
return
} }
switch msg.SubType { for _, p := range ps.apps {
case WSProviderSubTypeLogout: ps.log.WithField("provider", p.Host).Debug("Logging out")
for _, p := range ps.apps { err := p.Logout(ctx, func(c application.Claims) bool {
ps.log.WithField("provider", p.Host).Debug("Logging out") return c.Sid == mmsg.SessionID
err := p.Logout(ctx, func(c application.Claims) bool { })
return c.Sid == msg.SessionID if err != nil {
}) ps.log.WithField("provider", p.Host).WithError(err).Warning("failed to logout")
if err != nil {
ps.log.WithField("provider", p.Host).WithError(err).Warning("failed to logout")
}
} }
default:
ps.log.WithField("sub_type", msg.SubType).Warning("invalid sub_type")
} }
return nil
} }

View File

@ -6,7 +6,6 @@ import (
"strconv" "strconv"
"sync" "sync"
"github.com/mitchellh/mapstructure"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/wwt/guac" "github.com/wwt/guac"
@ -30,7 +29,7 @@ func NewServer(ac *ak.APIController) ak.Outpost {
connm: sync.RWMutex{}, connm: sync.RWMutex{},
conns: map[string]connection.Connection{}, conns: map[string]connection.Connection{},
} }
ac.AddWSHandler(rs.wsHandler) ac.AddEventHandler(rs.wsHandler)
return rs return rs
} }
@ -52,12 +51,14 @@ func parseIntOrZero(input string) int {
return x return x
} }
func (rs *RACServer) wsHandler(ctx context.Context, args map[string]interface{}) { func (rs *RACServer) wsHandler(ctx context.Context, msg ak.Event) error {
if msg.Instruction != ak.EventKindProviderSpecific {
return nil
}
wsm := WSMessage{} wsm := WSMessage{}
err := mapstructure.Decode(args, &wsm) err := msg.ArgsAs(&wsm)
if err != nil { if err != nil {
rs.log.WithError(err).Warning("invalid ws message") return err
return
} }
config := guac.NewGuacamoleConfiguration() config := guac.NewGuacamoleConfiguration()
config.Protocol = wsm.Protocol config.Protocol = wsm.Protocol
@ -71,23 +72,23 @@ func (rs *RACServer) wsHandler(ctx context.Context, args map[string]interface{})
} }
cc, err := connection.NewConnection(rs.ac, wsm.DestChannelID, config) cc, err := connection.NewConnection(rs.ac, wsm.DestChannelID, config)
if err != nil { if err != nil {
rs.log.WithError(err).Warning("failed to setup connection") return err
return
} }
cc.OnError = func(err error) { cc.OnError = func(err error) {
rs.connm.Lock() rs.connm.Lock()
delete(rs.conns, wsm.ConnID) delete(rs.conns, wsm.ConnID)
_ = rs.ac.SendWSHello(map[string]interface{}{ _ = rs.ac.SendEventHello(map[string]interface{}{
"active_connections": len(rs.conns), "active_connections": len(rs.conns),
}) })
rs.connm.Unlock() rs.connm.Unlock()
} }
rs.connm.Lock() rs.connm.Lock()
rs.conns[wsm.ConnID] = *cc rs.conns[wsm.ConnID] = *cc
_ = rs.ac.SendWSHello(map[string]interface{}{ _ = rs.ac.SendEventHello(map[string]interface{}{
"active_connections": len(rs.conns), "active_connections": len(rs.conns),
}) })
rs.connm.Unlock() rs.connm.Unlock()
return nil
} }
func (rs *RACServer) Start() error { func (rs *RACServer) Start() error {

View File

@ -2,6 +2,7 @@ package radius
import ( import (
"crypto/sha512" "crypto/sha512"
"encoding/hex"
"time" "time"
"github.com/getsentry/sentry-go" "github.com/getsentry/sentry-go"
@ -68,7 +69,9 @@ func (rs *RadiusServer) ServeRADIUS(w radius.ResponseWriter, r *radius.Request)
} }
} }
if pi == nil { if pi == nil {
nr.Log().WithField("hashed_secret", string(sha512.New().Sum(r.Secret))).Warning("No provider found") hs := sha512.Sum512([]byte(r.Secret))
bs := hex.EncodeToString(hs[:])
nr.Log().WithField("hashed_secret", bs).Warning("No provider found")
_ = w.Write(r.Response(radius.CodeAccessReject)) _ = w.Write(r.Response(radius.CodeAccessReject))
return return
} }

View File

@ -9,7 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"aws-cdk": "^2.1017.1", "aws-cdk": "^2.1018.1",
"cross-env": "^7.0.3" "cross-env": "^7.0.3"
}, },
"engines": { "engines": {
@ -17,16 +17,16 @@
} }
}, },
"node_modules/aws-cdk": { "node_modules/aws-cdk": {
"version": "2.1017.1", "version": "2.1018.1",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1017.1.tgz", "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1018.1.tgz",
"integrity": "sha512-KtDdkMhfVjDeexjpMrVoSlz2mTYI5BE/KotvJ7iFbZy1G0nkpW1ImZ54TdBefeeFmZ+8DAjU3I6nUFtymyOI1A==", "integrity": "sha512-kFPRox5kSm+ktJ451o0ng9rD+60p5Kt1CZIWw8kXnvqbsxN2xv6qbmyWSXw7sGVXVwqrRKVj+71/JeDr+LMAZw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"cdk": "bin/cdk" "cdk": "bin/cdk"
}, },
"engines": { "engines": {
"node": ">= 14.15.0" "node": ">= 18.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "2.3.2" "fsevents": "2.3.2"

View File

@ -10,7 +10,7 @@
"node": ">=20" "node": ">=20"
}, },
"devDependencies": { "devDependencies": {
"aws-cdk": "^2.1017.1", "aws-cdk": "^2.1018.1",
"cross-env": "^7.0.3" "cross-env": "^7.0.3"
} }
} }

View File

@ -26,7 +26,7 @@ Parameters:
Description: authentik Docker image Description: authentik Docker image
AuthentikVersion: AuthentikVersion:
Type: String Type: String
Default: 2025.6.0 Default: 2025.6.2
Description: authentik Docker image tag Description: authentik Docker image tag
AuthentikServerCPU: AuthentikServerCPU:
Type: Number Type: Number

View File

@ -6,18 +6,18 @@
# Translators: # Translators:
# jcamat, 2022 # jcamat, 2022
# Angel, 2024 # Angel, 2024
# Iamanaws, 2024
# Marcelo Elizeche Landó, 2025 # Marcelo Elizeche Landó, 2025
# Jens L. <jens@goauthentik.io>, 2025 # Jens L. <jens@goauthentik.io>, 2025
# Iamanaws, 2025
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-28 11:25+0000\n" "POT-Creation-Date: 2025-06-04 00:12+0000\n"
"PO-Revision-Date: 2022-09-26 16:47+0000\n" "PO-Revision-Date: 2022-09-26 16:47+0000\n"
"Last-Translator: Jens L. <jens@goauthentik.io>, 2025\n" "Last-Translator: Iamanaws, 2025\n"
"Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n" "Language-Team: Spanish (https://app.transifex.com/authentik/teams/119923/es/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -111,7 +111,7 @@ msgstr "Certificado Web usado por el servidor web Core de authentik"
#: authentik/brands/models.py #: authentik/brands/models.py
msgid "Certificates used for client authentication." msgid "Certificates used for client authentication."
msgstr "" msgstr "Certificados utilizados para la autenticación del cliente."
#: authentik/brands/models.py #: authentik/brands/models.py
msgid "Brand" msgid "Brand"
@ -131,7 +131,7 @@ msgstr "Descripción adicional no disponible."
#: authentik/core/api/groups.py #: authentik/core/api/groups.py
msgid "Cannot set group as parent of itself." msgid "Cannot set group as parent of itself."
msgstr "No se puede establecer el grupo como padre de sí mismo." msgstr "No se puede establecer un grupo como su propio padre."
#: authentik/core/api/providers.py #: authentik/core/api/providers.py
msgid "" msgid ""
@ -183,11 +183,11 @@ msgstr "Remueve usuario del grupo"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Enable superuser status" msgid "Enable superuser status"
msgstr "Habiliar estado de \"superusuario\"" msgstr "Habilitar el estado de superusuario"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Disable superuser status" msgid "Disable superuser status"
msgstr "Deshabiliar estado de \"superusuario\"" msgstr "Deshabilitar el estado de superusuario"
#: authentik/core/models.py #: authentik/core/models.py
msgid "User's display name." msgid "User's display name."
@ -241,7 +241,7 @@ msgstr "Flujo utilizado al autorizar a este proveedor."
#: authentik/core/models.py #: authentik/core/models.py
msgid "Flow used ending the session from a provider." msgid "Flow used ending the session from a provider."
msgstr "Flujo usado para terminar la sesión de un proveedor." msgstr "Flujo utilizado para finalizar la sesión desde un proveedor."
#: authentik/core/models.py #: authentik/core/models.py
msgid "" msgid ""
@ -273,11 +273,11 @@ msgstr "Aplicaciones"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Application Entitlement" msgid "Application Entitlement"
msgstr "" msgstr "Derecho de Aplicación"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Application Entitlements" msgid "Application Entitlements"
msgstr "" msgstr "Derechos de Aplicación"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Use the source-specific identifier" msgid "Use the source-specific identifier"
@ -288,9 +288,9 @@ msgid ""
"Link to a user with identical email address. Can have security implications " "Link to a user with identical email address. Can have security implications "
"when a source doesn't validate email addresses." "when a source doesn't validate email addresses."
msgstr "" msgstr ""
"Apunta a un usuario con una dirección de correo electrónico idéntica. Puede " "Enlace a un usuario con la misma dirección de correo electrónico. Puede "
"tener implicaciones de seguridad cuando una fuente no valida la dirección de" "tener implicaciones de seguridad cuando una fuente no valida las direcciones"
" correo electrónico." " de correo electrónico."
#: authentik/core/models.py #: authentik/core/models.py
msgid "" msgid ""
@ -305,8 +305,8 @@ msgid ""
"Link to a user with identical username. Can have security implications when " "Link to a user with identical username. Can have security implications when "
"a username is used with another source." "a username is used with another source."
msgstr "" msgstr ""
"Enlace a un usuario con un nombre de usuario idéntico. Puede tener " "Enlace a un usuario con el mismo nombre de usuario. Puede tener "
"implicaciones de seguridad cuando se usa un nombre de usuario con otra " "implicaciones de seguridad cuando un nombre de usuario se utiliza con otra "
"fuente." "fuente."
#: authentik/core/models.py #: authentik/core/models.py
@ -322,8 +322,8 @@ msgid ""
"Link to a group with identical name. Can have security implications when a " "Link to a group with identical name. Can have security implications when a "
"group name is used with another source." "group name is used with another source."
msgstr "" msgstr ""
"Enlace a un grupo con un nombre idéntico. Puede tener implicaciones de " "Enlace a un grupo con el mismo nombre. Puede tener implicaciones de "
"seguridad cuando se utiliza un nombre de grupo con otra fuente." "seguridad cuando un nombre de grupo se utiliza con otra fuente."
#: authentik/core/models.py #: authentik/core/models.py
msgid "Use the group name, but deny enrollment when the name already exists." msgid "Use the group name, but deny enrollment when the name already exists."
@ -385,7 +385,7 @@ msgstr "Asignaciones de Propiedades"
#: authentik/core/models.py #: authentik/core/models.py
msgid "session data" msgid "session data"
msgstr "" msgstr "datos de sesión"
#: authentik/core/models.py #: authentik/core/models.py
msgid "Session" msgid "Session"
@ -424,7 +424,7 @@ msgstr "¡Autenticado exitosamente con {source}!"
#: authentik/core/sources/flow_manager.py #: authentik/core/sources/flow_manager.py
#, python-brace-format #, python-brace-format
msgid "Successfully linked {source}!" msgid "Successfully linked {source}!"
msgstr "¡{source} vinculado exitosamente!" msgstr "¡{source} enlazado correctamente!"
#: authentik/core/sources/flow_manager.py #: authentik/core/sources/flow_manager.py
msgid "Source is not configured for enrollment." msgid "Source is not configured for enrollment."
@ -476,11 +476,11 @@ msgstr ""
#: authentik/crypto/models.py #: authentik/crypto/models.py
msgid "Certificate-Key Pair" msgid "Certificate-Key Pair"
msgstr "Par de claves de certificado" msgstr "Par Certificado-Clave"
#: authentik/crypto/models.py #: authentik/crypto/models.py
msgid "Certificate-Key Pairs" msgid "Certificate-Key Pairs"
msgstr "Pares de claves de certificado" msgstr "Pares Certificado-Clave"
#: authentik/enterprise/api.py #: authentik/enterprise/api.py
msgid "Enterprise is required to create/update this object." msgid "Enterprise is required to create/update this object."
@ -511,7 +511,7 @@ msgstr ""
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
msgid "Number of passwords to check against." msgid "Number of passwords to check against."
msgstr "" msgstr "Número de contraseñas contra las que verificar."
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
#: authentik/policies/password/models.py #: authentik/policies/password/models.py
@ -521,18 +521,20 @@ msgstr "La contraseña no se ha establecido en contexto"
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
msgid "This password has been used previously. Please choose a different one." msgid "This password has been used previously. Please choose a different one."
msgstr "" msgstr ""
"Esta contraseña se ha utilizado anteriormente. Por favor, elija una "
"diferente."
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policy" msgid "Password Uniqueness Policy"
msgstr "" msgstr "Política de Unicidad de Contraseñas"
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
msgid "Password Uniqueness Policies" msgid "Password Uniqueness Policies"
msgstr "" msgstr "Políticas de Unicidad de Contraseñas"
#: authentik/enterprise/policies/unique_password/models.py #: authentik/enterprise/policies/unique_password/models.py
msgid "User Password History" msgid "User Password History"
msgstr "" msgstr "Historial de Contraseñas del Usuario"
#: authentik/enterprise/policy.py #: authentik/enterprise/policy.py
msgid "Enterprise required to access this feature." msgid "Enterprise required to access this feature."
@ -617,39 +619,39 @@ msgstr "Clave de firma"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Key used to sign the SSF Events." msgid "Key used to sign the SSF Events."
msgstr "" msgstr "Clave utilizada para firmar los eventos SSF."
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Provider" msgid "Shared Signals Framework Provider"
msgstr "" msgstr "Proveedor del Marco de Señales Compartidas"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Shared Signals Framework Providers" msgid "Shared Signals Framework Providers"
msgstr "" msgstr "Proveedores del Marco de Señales Compartidas"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "Add stream to SSF provider" msgid "Add stream to SSF provider"
msgstr "" msgstr "Agregar flujo de datos al proveedor SSF"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream" msgid "SSF Stream"
msgstr "" msgstr "Flujo de Datos SSF"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Streams" msgid "SSF Streams"
msgstr "" msgstr "Flujos de Datos SSF"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Event" msgid "SSF Stream Event"
msgstr "" msgstr "Evento de Flujo de Datos SSF"
#: authentik/enterprise/providers/ssf/models.py #: authentik/enterprise/providers/ssf/models.py
msgid "SSF Stream Events" msgid "SSF Stream Events"
msgstr "" msgstr "Eventos de Flujos de Datos SSF"
#: authentik/enterprise/providers/ssf/tasks.py #: authentik/enterprise/providers/ssf/tasks.py
msgid "Failed to send request" msgid "Failed to send request"
msgstr "Falló envio de petición" msgstr "Error al enviar la solicitud"
#: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py #: authentik/enterprise/stages/authenticator_endpoint_gdtc/models.py
msgid "Endpoint Authenticator Google Device Trust Connector Stage" msgid "Endpoint Authenticator Google Device Trust Connector Stage"
@ -681,26 +683,29 @@ msgid ""
"option has a higher priority than the `client_certificate` option on " "option has a higher priority than the `client_certificate` option on "
"`Brand`." "`Brand`."
msgstr "" msgstr ""
"Configura las autoridades certificadoras para validar el certificado. Esta "
"opción tiene una prioridad mayor que la opción `client_certificate` en "
"`Brand`."
#: authentik/enterprise/stages/mtls/models.py #: authentik/enterprise/stages/mtls/models.py
msgid "Mutual TLS Stage" msgid "Mutual TLS Stage"
msgstr "" msgstr "Etapa de TLS mutuo"
#: authentik/enterprise/stages/mtls/models.py #: authentik/enterprise/stages/mtls/models.py
msgid "Mutual TLS Stages" msgid "Mutual TLS Stages"
msgstr "" msgstr "Etapas de TLS mutuo"
#: authentik/enterprise/stages/mtls/models.py #: authentik/enterprise/stages/mtls/models.py
msgid "Permissions to pass Certificates for outposts." msgid "Permissions to pass Certificates for outposts."
msgstr "" msgstr "Permisos para pasar Certificados a los puestos avanzados."
#: authentik/enterprise/stages/mtls/stage.py #: authentik/enterprise/stages/mtls/stage.py
msgid "Certificate required but no certificate was given." msgid "Certificate required but no certificate was given."
msgstr "" msgstr "Se requiere certificado, pero no se proporcionó ninguno."
#: authentik/enterprise/stages/mtls/stage.py #: authentik/enterprise/stages/mtls/stage.py
msgid "No user found for certificate." msgid "No user found for certificate."
msgstr "" msgstr "No se encontró usuario para el certificado."
#: authentik/enterprise/stages/source/models.py #: authentik/enterprise/stages/source/models.py
msgid "" msgid ""
@ -753,12 +758,16 @@ msgid ""
"Customize the body of the request. Mapping should return data that is JSON-" "Customize the body of the request. Mapping should return data that is JSON-"
"serializable." "serializable."
msgstr "" msgstr ""
"Personaliza el cuerpo de la solicitud. El mapeo debe devolver datos que sean"
" serializables en JSON."
#: authentik/events/models.py #: authentik/events/models.py
msgid "" msgid ""
"Configure additional headers to be sent. Mapping should return a dictionary " "Configure additional headers to be sent. Mapping should return a dictionary "
"of key-value pairs" "of key-value pairs"
msgstr "" msgstr ""
"Configura encabezados adicionales para enviar. El mapeo debe devolver un "
"diccionario de pares clave-valor"
#: authentik/events/models.py #: authentik/events/models.py
msgid "" msgid ""
@ -786,7 +795,7 @@ msgstr "Transporte de notificaciones"
#: authentik/events/models.py #: authentik/events/models.py
msgid "Notification Transports" msgid "Notification Transports"
msgstr "Transportes de notificación" msgstr "Medios de Notificación"
#: authentik/events/models.py #: authentik/events/models.py
msgid "Notice" msgid "Notice"
@ -813,9 +822,9 @@ msgid ""
"Select which transports should be used to notify the user. If none are " "Select which transports should be used to notify the user. If none are "
"selected, the notification will only be shown in the authentik UI." "selected, the notification will only be shown in the authentik UI."
msgstr "" msgstr ""
"Seleccione qué transportes se deben usar para notificar al usuario. Si no se" "Selecciona qué medios se deben usar para notificar al usuario. Si no se "
" selecciona ninguno, la notificación solo se mostrará en la interfaz de " "selecciona ninguno, la notificación solo se mostrará en la interfaz de "
"usuario de authentik." "authentik."
#: authentik/events/models.py #: authentik/events/models.py
msgid "Controls which severity level the created notifications will have." msgid "Controls which severity level the created notifications will have."
@ -987,7 +996,7 @@ msgstr "Evalúa políticas durante el proceso de planeación del Flujo."
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "Evaluate policies when the Stage is presented to the user." msgid "Evaluate policies when the Stage is presented to the user."
msgstr "" msgstr "Evaluar las políticas cuando la Etapa se presenta al usuario."
#: authentik/flows/models.py #: authentik/flows/models.py
msgid "" msgid ""
@ -1034,6 +1043,8 @@ msgid ""
"When enabled, provider will not modify or create objects in the remote " "When enabled, provider will not modify or create objects in the remote "
"system." "system."
msgstr "" msgstr ""
"Cuando está habilitado, el proveedor no modificará ni creará objetos en el "
"sistema remoto."
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
msgid "Starting full provider sync" msgid "Starting full provider sync"
@ -1041,20 +1052,21 @@ msgstr "Iniciando sincronización completa de proveedor"
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing users" msgid "Syncing users"
msgstr "" msgstr "Sincronizando usuarios"
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
msgid "Syncing groups" msgid "Syncing groups"
msgstr "" msgstr "Sincronizando grupos"
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format #, python-brace-format
msgid "Syncing page {page} of groups" msgid "Syncing page {page} of {object_type}"
msgstr "Sincronizando página {page} de grupos" msgstr "Sincronizando página {page} de {object_type}"
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
msgid "Dropping mutating request due to dry run" msgid "Dropping mutating request due to dry run"
msgstr "" msgstr ""
"Descartando solicitud de mutación debido a ejecución en modo de simulación"
#: authentik/lib/sync/outgoing/tasks.py #: authentik/lib/sync/outgoing/tasks.py
#, python-brace-format #, python-brace-format
@ -1233,7 +1245,7 @@ msgstr ""
#: authentik/policies/expiry/models.py #: authentik/policies/expiry/models.py
msgid "Password has expired." msgid "Password has expired."
msgstr "La contraseña ha caducado." msgstr "La contraseña ha expirado."
#: authentik/policies/expiry/models.py #: authentik/policies/expiry/models.py
msgid "Password Expiry Policy" msgid "Password Expiry Policy"
@ -1271,7 +1283,7 @@ msgstr "La IP del cliente no está en un país permitido."
#: authentik/policies/geoip/models.py #: authentik/policies/geoip/models.py
msgid "Distance from previous authentication is larger than threshold." msgid "Distance from previous authentication is larger than threshold."
msgstr "La distancia desde la autenticación previa es mayor que el límite." msgstr "La distancia desde la autenticación anterior es mayor que el umbral."
#: authentik/policies/geoip/models.py #: authentik/policies/geoip/models.py
msgid "Distance is further than possible." msgid "Distance is further than possible."
@ -1320,7 +1332,7 @@ msgstr "Vinculación de Políticas"
#: authentik/policies/models.py #: authentik/policies/models.py
msgid "Policy Bindings" msgid "Policy Bindings"
msgstr "Vinculaciones de políticas" msgstr "Vinculaciones de Políticas"
#: authentik/policies/models.py #: authentik/policies/models.py
msgid "" msgid ""
@ -1594,11 +1606,11 @@ msgstr "ES256 (Encriptación Asimétrica)"
#: authentik/providers/oauth2/models.py #: authentik/providers/oauth2/models.py
msgid "ES384 (Asymmetric Encryption)" msgid "ES384 (Asymmetric Encryption)"
msgstr "" msgstr "ES384 (Encriptación Asimétrica)"
#: authentik/providers/oauth2/models.py #: authentik/providers/oauth2/models.py
msgid "ES512 (Asymmetric Encryption)" msgid "ES512 (Asymmetric Encryption)"
msgstr "" msgstr "ES512 (Encriptación Asimétrica)"
#: authentik/providers/oauth2/models.py #: authentik/providers/oauth2/models.py
msgid "Scope used by the client" msgid "Scope used by the client"
@ -1813,7 +1825,7 @@ msgstr "Valida Certificados SSL de servidores de origen"
#: authentik/providers/proxy/models.py #: authentik/providers/proxy/models.py
msgid "Internal host SSL Validation" msgid "Internal host SSL Validation"
msgstr "Validación SSL de host interno" msgstr "Validación SSL del host interno"
#: authentik/providers/proxy/models.py #: authentik/providers/proxy/models.py
msgid "" msgid ""
@ -2027,7 +2039,7 @@ msgstr ""
#: authentik/providers/saml/models.py #: authentik/providers/saml/models.py
msgid "AuthnContextClassRef Property Mapping" msgid "AuthnContextClassRef Property Mapping"
msgstr "" msgstr "Asignación de Propiedades de AuthnContextClassRef"
#: authentik/providers/saml/models.py #: authentik/providers/saml/models.py
msgid "" msgid ""
@ -2035,6 +2047,9 @@ msgid ""
"empty, the AuthnContextClassRef will be set based on which authentication " "empty, the AuthnContextClassRef will be set based on which authentication "
"methods the user used to authenticate." "methods the user used to authenticate."
msgstr "" msgstr ""
"Configura cómo se creará el valor de AuthnContextClassRef. Si se deja vacío,"
" el AuthnContextClassRef se establecerá según los métodos de autenticación "
"que el usuario haya utilizado para autenticarse."
#: authentik/providers/saml/models.py #: authentik/providers/saml/models.py
msgid "" msgid ""
@ -2184,11 +2199,11 @@ msgstr "Predeterminado"
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "AWS" msgid "AWS"
msgstr "" msgstr "AWS"
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "Slack" msgid "Slack"
msgstr "" msgstr "Slack"
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "Base URL to SCIM requests, usually ends in /v2" msgid "Base URL to SCIM requests, usually ends in /v2"
@ -2200,11 +2215,13 @@ msgstr "Token de Autenticación"
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "SCIM Compatibility Mode" msgid "SCIM Compatibility Mode"
msgstr "" msgstr "Modo de Compatibilidad SCIM"
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "Alter authentik behavior for vendor-specific SCIM implementations." msgid "Alter authentik behavior for vendor-specific SCIM implementations."
msgstr "" msgstr ""
"Modificar el comportamiento de authentik para implementaciones SCIM "
"específicas de proveedores."
#: authentik/providers/scim/models.py #: authentik/providers/scim/models.py
msgid "SCIM Provider" msgid "SCIM Provider"
@ -2232,7 +2249,7 @@ msgstr "Roles"
#: authentik/rbac/models.py #: authentik/rbac/models.py
msgid "Initial Permissions" msgid "Initial Permissions"
msgstr "" msgstr "Permisos Iniciales"
#: authentik/rbac/models.py #: authentik/rbac/models.py
msgid "System permission" msgid "System permission"
@ -2270,7 +2287,7 @@ msgstr ""
#: authentik/recovery/views.py #: authentik/recovery/views.py
msgid "Used recovery-link to authenticate." msgid "Used recovery-link to authenticate."
msgstr "Se usó el enlace de recuperación para autenticarse." msgstr "Se utilizó un enlace de recuperación para autenticarse."
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "Kerberos realm" msgid "Kerberos realm"
@ -2282,7 +2299,7 @@ msgstr "krb5.conf personalizado a usar. Usa el del sistema por defecto."
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "KAdmin server type" msgid "KAdmin server type"
msgstr "" msgstr "Tipo de servidor KAdmin"
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "Sync users from Kerberos into authentik" msgid "Sync users from Kerberos into authentik"
@ -2290,23 +2307,24 @@ msgstr "Sincronizar usuarios desde Kerberos hacia Authentik"
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "When a user changes their password, sync it back to Kerberos" msgid "When a user changes their password, sync it back to Kerberos"
msgstr "Cuando un usuario cambia su contraseña, sincronizarlo hacia Kerberos" msgstr ""
"Cuando un usuario cambie su contraseña, sincronizarla de vuelta a Kerberos."
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "Principal to authenticate to kadmin for sync." msgid "Principal to authenticate to kadmin for sync."
msgstr "Principal para autenticarse como kadmin para la sincronización." msgstr "Principal para autenticarse en kadmin para la sincronización."
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "Password to authenticate to kadmin for sync" msgid "Password to authenticate to kadmin for sync"
msgstr "Contraseña para autenticarse como kadmin para la sincronización" msgstr "Contraseña para autenticarse en kadmin para la sincronización"
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "" msgid ""
"Keytab to authenticate to kadmin for sync. Must be base64-encoded or in the " "Keytab to authenticate to kadmin for sync. Must be base64-encoded or in the "
"form TYPE:residual" "form TYPE:residual"
msgstr "" msgstr ""
"Keytab para autenticarse como kadmin para la sincronización. Debe estar " "Keytab para autenticarse en kadmin para la sincronización. Debe estar "
"codificado en base64 o en el formato TIPO:residual" "codificado en base64 o en el formato TIPO:residuo"
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "" msgid ""
@ -2322,7 +2340,7 @@ msgid ""
"HTTP@hostname" "HTTP@hostname"
msgstr "" msgstr ""
"Forzar el uso de un nombre de servidor específico para SPNEGO. Debe estar en" "Forzar el uso de un nombre de servidor específico para SPNEGO. Debe estar en"
" el formato HTTP@nombredelservidor" " el formato HTTP@nombre_de_host"
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "SPNEGO keytab base64-encoded or path to keytab in the form FILE:path" msgid "SPNEGO keytab base64-encoded or path to keytab in the form FILE:path"
@ -2339,8 +2357,8 @@ msgid ""
"If enabled, the authentik-stored password will be updated upon login with " "If enabled, the authentik-stored password will be updated upon login with "
"the Kerberos password backend" "the Kerberos password backend"
msgstr "" msgstr ""
"Si está habilitado, la contraseña almacenada por authentik se actualizada " "Si está habilitado, la contraseña almacenada en authentik se actualizará al "
"al iniciar sesión con el backend de contraseñas Kerberos" "iniciar sesión con el backend de contraseñas de Kerberos."
#: authentik/sources/kerberos/models.py #: authentik/sources/kerberos/models.py
msgid "Kerberos Source" msgid "Kerberos Source"
@ -2388,7 +2406,7 @@ msgid ""
msgstr "" msgstr ""
"\n" "\n"
" Asegúrate de que tienes entradas válidas\n" " Asegúrate de que tienes entradas válidas\n"
" (se obtienen a través de kinit) \n" " (obtenibles mediante kinit) \n"
" y de haber configurado correctamente el navegador.\n" " y de haber configurado correctamente el navegador.\n"
" Por favor, contacta a tu administrador.\n" " Por favor, contacta a tu administrador.\n"
" " " "
@ -2453,6 +2471,10 @@ msgstr "DN de grupo de adición"
msgid "Consider Objects matching this filter to be Users." msgid "Consider Objects matching this filter to be Users."
msgstr "Considere que los objetos que coinciden con este filtro son usuarios." msgstr "Considere que los objetos que coinciden con este filtro son usuarios."
#: authentik/sources/ldap/models.py
msgid "Attribute which matches the value of `group_membership_field`."
msgstr "Atributo que coincide con el valor de `group_membership_field`."
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "Field which contains members of a group." msgid "Field which contains members of a group."
msgstr "Campo que contiene los miembros de un grupo." msgstr "Campo que contiene los miembros de un grupo."
@ -2485,12 +2507,17 @@ msgid ""
"attribute. This allows nested group resolution on systems like FreeIPA and " "attribute. This allows nested group resolution on systems like FreeIPA and "
"Active Directory" "Active Directory"
msgstr "" msgstr ""
"Buscar la pertenencia a grupos basándose en un atributo del usuario en lugar"
" de un atributo del grupo. Esto permite la resolución de grupos anidados en "
"sistemas como FreeIPA y Active Directory"
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "" msgid ""
"Delete authentik users and groups which were previously supplied by this " "Delete authentik users and groups which were previously supplied by this "
"source, but are now missing from it." "source, but are now missing from it."
msgstr "" msgstr ""
"Eliminar usuarios y grupos de authentik que fueron proporcionados "
"previamente por esta fuente, pero que ahora están ausentes."
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "LDAP Source" msgid "LDAP Source"
@ -2512,22 +2539,24 @@ msgstr "Asignaciones de Propiedades de Fuente de LDAP"
msgid "" msgid ""
"Unique ID used while checking if this object still exists in the directory." "Unique ID used while checking if this object still exists in the directory."
msgstr "" msgstr ""
"ID único utilizado para verificar si este objeto aún existe en el "
"directorio."
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "User LDAP Source Connection" msgid "User LDAP Source Connection"
msgstr "" msgstr "Conexión de Fuente LDAP de Usuario"
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "User LDAP Source Connections" msgid "User LDAP Source Connections"
msgstr "" msgstr "Conexiones de Fuente LDAP de Usuario"
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connection" msgid "Group LDAP Source Connection"
msgstr "" msgstr "Conexión de Fuente LDAP de Grupo"
#: authentik/sources/ldap/models.py #: authentik/sources/ldap/models.py
msgid "Group LDAP Source Connections" msgid "Group LDAP Source Connections"
msgstr "" msgstr "Conexiones de Fuente LDAP de Grupo"
#: authentik/sources/ldap/signals.py #: authentik/sources/ldap/signals.py
msgid "Password does not match Active Directory Complexity." msgid "Password does not match Active Directory Complexity."
@ -2539,11 +2568,11 @@ msgstr "No se recibió ningún token."
#: authentik/sources/oauth/models.py #: authentik/sources/oauth/models.py
msgid "HTTP Basic Authentication" msgid "HTTP Basic Authentication"
msgstr "" msgstr "Autenticación Básica HTTP"
#: authentik/sources/oauth/models.py #: authentik/sources/oauth/models.py
msgid "Include the client ID and secret as request parameters" msgid "Include the client ID and secret as request parameters"
msgstr "" msgstr "Incluir el ID de cliente y el secreto como parámetros de la solicitud"
#: authentik/sources/oauth/models.py #: authentik/sources/oauth/models.py
msgid "Request Token URL" msgid "Request Token URL"
@ -2590,6 +2619,8 @@ msgid ""
"How to perform authentication during an authorization_code token request " "How to perform authentication during an authorization_code token request "
"flow" "flow"
msgstr "" msgstr ""
"Cómo realizar la autenticación durante un flujo de solicitud de token con "
"authorization_code"
#: authentik/sources/oauth/models.py #: authentik/sources/oauth/models.py
msgid "OAuth Source" msgid "OAuth Source"
@ -2907,7 +2938,7 @@ msgstr "Conexiones de Fuente de SAML de Grupo"
#: authentik/sources/saml/views.py #: authentik/sources/saml/views.py
#, python-brace-format #, python-brace-format
msgid "Continue to {source_name}" msgid "Continue to {source_name}"
msgstr "" msgstr "Continuar a {source_name}"
#: authentik/sources/scim/models.py #: authentik/sources/scim/models.py
msgid "SCIM Source" msgid "SCIM Source"
@ -2943,7 +2974,7 @@ msgstr "Dispositivos Duo"
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
msgid "Email OTP" msgid "Email OTP"
msgstr "" msgstr "OTP por Correo Electrónico"
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
#: authentik/stages/email/models.py #: authentik/stages/email/models.py
@ -2964,11 +2995,11 @@ msgstr ""
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
msgid "Email Authenticator Setup Stage" msgid "Email Authenticator Setup Stage"
msgstr "" msgstr "Etapa de Configuración del Autenticador de Correo Electrónico"
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
msgid "Email Authenticator Setup Stages" msgid "Email Authenticator Setup Stages"
msgstr "" msgstr "Etapas de Configuración del Autenticador de Correo Electrónico"
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
#: authentik/stages/authenticator_email/stage.py #: authentik/stages/authenticator_email/stage.py
@ -2979,11 +3010,11 @@ msgstr ""
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
msgid "Email Device" msgid "Email Device"
msgstr "Dispositivo de Email" msgstr "Dispositivo de correo electrónico"
#: authentik/stages/authenticator_email/models.py #: authentik/stages/authenticator_email/models.py
msgid "Email Devices" msgid "Email Devices"
msgstr "Dispositivos de Email" msgstr "Dispositivos de correo electrónico"
#: authentik/stages/authenticator_email/stage.py #: authentik/stages/authenticator_email/stage.py
#: authentik/stages/authenticator_sms/stage.py #: authentik/stages/authenticator_sms/stage.py
@ -2993,7 +3024,7 @@ msgstr "El código no coincide"
#: authentik/stages/authenticator_email/stage.py #: authentik/stages/authenticator_email/stage.py
msgid "Invalid email" msgid "Invalid email"
msgstr "Email Inválido" msgstr "Correo electrónico inválido"
#: authentik/stages/authenticator_email/templates/email/email_otp.html #: authentik/stages/authenticator_email/templates/email/email_otp.html
#: authentik/stages/email/templates/email/password_reset.html #: authentik/stages/email/templates/email/password_reset.html
@ -3013,6 +3044,9 @@ msgid ""
" Email MFA code.\n" " Email MFA code.\n"
" " " "
msgstr "" msgstr ""
"\n"
" Código MFA por correo electrónico.\n"
" "
#: authentik/stages/authenticator_email/templates/email/email_otp.html #: authentik/stages/authenticator_email/templates/email/email_otp.html
#, python-format #, python-format
@ -3022,7 +3056,8 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
"Si no solicitaste este código, por favor ignora este correo. El código anterior es válido por %(expires)s." " Si no solicitaste este código, por favor ignora este correo. El código anterior es válido por %(expires)s.\n"
" "
#: authentik/stages/authenticator_email/templates/email/email_otp.txt #: authentik/stages/authenticator_email/templates/email/email_otp.txt
#: authentik/stages/email/templates/email/password_reset.txt #: authentik/stages/email/templates/email/password_reset.txt
@ -3035,6 +3070,8 @@ msgid ""
"\n" "\n"
"Email MFA code\n" "Email MFA code\n"
msgstr "" msgstr ""
"\n"
"Código MFA por correo electrónico\n"
#: authentik/stages/authenticator_email/templates/email/email_otp.txt #: authentik/stages/authenticator_email/templates/email/email_otp.txt
#, python-format #, python-format
@ -3276,8 +3313,8 @@ msgstr "No se pudo validar el token"
msgid "" msgid ""
"Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)." "Offset after which consent expires. (Format: hours=1;minutes=2;seconds=3)."
msgstr "" msgstr ""
"Compensación después de la cual caduca el consentimiento. (Formato: horas = " "Desfase después del cual expira el consentimiento. (Formato: "
"1; minutos = 2; segundos = 3)." "hours=1;minutes=2;seconds=3)."
#: authentik/stages/consent/models.py #: authentik/stages/consent/models.py
msgid "Consent Stage" msgid "Consent Stage"
@ -3297,7 +3334,7 @@ msgstr "Consentimientos del usuario"
#: authentik/stages/consent/stage.py #: authentik/stages/consent/stage.py
msgid "Invalid consent token, re-showing prompt" msgid "Invalid consent token, re-showing prompt"
msgstr "" msgstr "Token de consentimiento inválido, mostrando el aviso nuevamente"
#: authentik/stages/deny/models.py #: authentik/stages/deny/models.py
msgid "Deny Stage" msgid "Deny Stage"
@ -3317,11 +3354,11 @@ msgstr "Etapas ficticias"
#: authentik/stages/email/flow.py #: authentik/stages/email/flow.py
msgid "Continue to confirm this email address." msgid "Continue to confirm this email address."
msgstr "" msgstr "Continúa para confirmar esta dirección de correo electrónico."
#: authentik/stages/email/flow.py #: authentik/stages/email/flow.py
msgid "Link was already used, please request a new link." msgid "Link was already used, please request a new link."
msgstr "" msgstr "El enlace ya fue utilizado, por favor, solícita uno nuevo."
#: authentik/stages/email/models.py #: authentik/stages/email/models.py
msgid "Password Reset" msgid "Password Reset"
@ -3445,7 +3482,8 @@ msgid ""
" " " "
msgstr "" msgstr ""
"\n" "\n"
"Si no solicitaste un cambio de contraseña, por favor ignora este correo. El enlace anterior es válido por %(expires)s." " Si no solicitaste un cambio de contraseña, por favor ignora este correo. El enlace anterior es válido por %(expires)s.\n"
" "
#: authentik/stages/email/templates/email/password_reset.txt #: authentik/stages/email/templates/email/password_reset.txt
msgid "" msgid ""
@ -3529,24 +3567,26 @@ msgid ""
"Show the user the 'Remember me on this device' toggle, allowing repeat users" "Show the user the 'Remember me on this device' toggle, allowing repeat users"
" to skip straight to entering their password." " to skip straight to entering their password."
msgstr "" msgstr ""
"Mostrar al usuario la opción \"Recordarme en este dispositivo\", permitiendo"
" que los usuarios recurrentes pasen directamente a ingresar su contraseña."
#: authentik/stages/identification/models.py #: authentik/stages/identification/models.py
msgid "Optional enrollment flow, which is linked at the bottom of the page." msgid "Optional enrollment flow, which is linked at the bottom of the page."
msgstr "" msgstr ""
"Flujo de inscripción opcional, que está vinculado en la parte inferior de la" "Flujo de inscripción opcional, que se enlaza en la parte inferior de la "
" página." "página."
#: authentik/stages/identification/models.py #: authentik/stages/identification/models.py
msgid "Optional recovery flow, which is linked at the bottom of the page." msgid "Optional recovery flow, which is linked at the bottom of the page."
msgstr "" msgstr ""
"Flujo de recuperación opcional, que está vinculado en la parte inferior de " "Flujo de recuperación opcional, que se enlaza en la parte inferior de la "
"la página." "página."
#: authentik/stages/identification/models.py #: authentik/stages/identification/models.py
msgid "Optional passwordless flow, which is linked at the bottom of the page." msgid "Optional passwordless flow, which is linked at the bottom of the page."
msgstr "" msgstr ""
"Flujo sin contraseña opcional, el cual está vinculado en la parte inferior " "Flujo opcional sin contraseña, que se enlaza en la parte inferior de la "
"de la página." "página."
#: authentik/stages/identification/models.py #: authentik/stages/identification/models.py
msgid "Specify which sources should be shown." msgid "Specify which sources should be shown."
@ -3780,11 +3820,11 @@ msgstr "Las contraseñas no coinciden."
#: authentik/stages/redirect/api.py #: authentik/stages/redirect/api.py
msgid "Target URL should be present when mode is Static." msgid "Target URL should be present when mode is Static."
msgstr "" msgstr "La URL de destino debe estar presente cuando el modo es Estático."
#: authentik/stages/redirect/api.py #: authentik/stages/redirect/api.py
msgid "Target Flow should be present when mode is Flow." msgid "Target Flow should be present when mode is Flow."
msgstr "" msgstr "El Flujo de Destino debe estar presente cuando el modo es Flujo."
#: authentik/stages/redirect/models.py #: authentik/stages/redirect/models.py
msgid "Redirect Stage" msgid "Redirect Stage"
@ -3841,10 +3881,6 @@ msgstr "Etapas de inicio de"
msgid "No Pending user to login." msgid "No Pending user to login."
msgstr "Ningún usuario pendiente para iniciar sesión." msgstr "Ningún usuario pendiente para iniciar sesión."
#: authentik/stages/user_login/stage.py
msgid "Successfully logged in!"
msgstr "¡Se ha iniciado sesión correctamente!"
#: authentik/stages/user_logout/models.py #: authentik/stages/user_logout/models.py
msgid "User Logout Stage" msgid "User Logout Stage"
msgstr "Etapa de cierre de sesión del usuario" msgstr "Etapa de cierre de sesión del usuario"
@ -3920,10 +3956,12 @@ msgstr ""
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Reputation cannot decrease lower than this value. Zero or negative." msgid "Reputation cannot decrease lower than this value. Zero or negative."
msgstr "" msgstr ""
"La reputación no puede disminuir por debajo de este valor. Cero o negativo."
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Reputation cannot increase higher than this value. Zero or positive." msgid "Reputation cannot increase higher than this value. Zero or positive."
msgstr "" msgstr ""
"La reputación no puede aumentar por encima de este valor. Cero o positivo."
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "The option configures the footer links on the flow executor pages." msgid "The option configures the footer links on the flow executor pages."
@ -3946,8 +3984,8 @@ msgstr "Personificación habilitada/deshabilitada globalmente."
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Require administrators to provide a reason for impersonating a user." msgid "Require administrators to provide a reason for impersonating a user."
msgstr "" msgstr ""
"Requerir a los administradores proporcionar una razón para suplantar un " "Requerir que los administradores proporcionen una razón para personificar a "
"usuario." "un usuario."
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Default token duration" msgid "Default token duration"
@ -3959,7 +3997,7 @@ msgstr "Longitud predeterminada del token"
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Tenant" msgid "Tenant"
msgstr "inquilino" msgstr "Inquilino"
#: authentik/tenants/models.py #: authentik/tenants/models.py
msgid "Tenants" msgid "Tenants"

Binary file not shown.

Binary file not shown.

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.0", "version": "2025.6.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.0", "version": "2025.6.2",
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^5.2.2", "@trivago/prettier-plugin-sort-imports": "^5.2.2",
"prettier": "^3.3.3", "prettier": "^3.3.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@goauthentik/authentik", "name": "@goauthentik/authentik",
"version": "2025.6.0", "version": "2025.6.2",
"private": true, "private": true,
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {

View File

@ -216,9 +216,9 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.20.0", "version": "0.20.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/object-schema": "^2.1.6", "@eslint/object-schema": "^2.1.6",
@ -274,9 +274,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -576,17 +576,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz",
"integrity": "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ==", "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/type-utils": "8.33.0", "@typescript-eslint/type-utils": "8.34.1",
"@typescript-eslint/utils": "8.33.0", "@typescript-eslint/utils": "8.34.1",
"@typescript-eslint/visitor-keys": "8.33.0", "@typescript-eslint/visitor-keys": "8.34.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^7.0.0", "ignore": "^7.0.0",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -600,7 +600,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/parser": "^8.33.0", "@typescript-eslint/parser": "^8.34.1",
"eslint": "^8.57.0 || ^9.0.0", "eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0" "typescript": ">=4.8.4 <5.9.0"
} }
@ -616,16 +616,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz",
"integrity": "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ==", "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.33.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.33.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/visitor-keys": "8.33.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -641,14 +641,14 @@
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz",
"integrity": "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A==", "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.33.0", "@typescript-eslint/tsconfig-utils": "^8.34.1",
"@typescript-eslint/types": "^8.33.0", "@typescript-eslint/types": "^8.34.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -657,17 +657,20 @@
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz",
"integrity": "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw==", "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.33.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.33.0" "@typescript-eslint/visitor-keys": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -678,9 +681,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz",
"integrity": "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug==", "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -695,14 +698,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz",
"integrity": "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ==", "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "8.33.0", "@typescript-eslint/typescript-estree": "8.34.1",
"@typescript-eslint/utils": "8.33.0", "@typescript-eslint/utils": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.1.0" "ts-api-utils": "^2.1.0"
}, },
@ -719,9 +722,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz",
"integrity": "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg==", "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -733,16 +736,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz",
"integrity": "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ==", "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.33.0", "@typescript-eslint/project-service": "8.34.1",
"@typescript-eslint/tsconfig-utils": "8.33.0", "@typescript-eslint/tsconfig-utils": "8.34.1",
"@typescript-eslint/types": "8.33.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/visitor-keys": "8.33.0", "@typescript-eslint/visitor-keys": "8.34.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -762,9 +765,9 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -801,16 +804,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz",
"integrity": "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw==", "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.7.0", "@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/scope-manager": "8.34.1",
"@typescript-eslint/types": "8.33.0", "@typescript-eslint/types": "8.34.1",
"@typescript-eslint/typescript-estree": "8.33.0" "@typescript-eslint/typescript-estree": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -825,14 +828,14 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz",
"integrity": "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ==", "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.33.0", "@typescript-eslint/types": "8.34.1",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -843,9 +846,9 @@
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.14.1", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@ -1551,18 +1554,18 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.28.0", "version": "9.29.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
"integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0", "@eslint/config-array": "^0.20.1",
"@eslint/config-helpers": "^0.2.1", "@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.14.0", "@eslint/core": "^0.14.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.28.0", "@eslint/js": "9.29.0",
"@eslint/plugin-kit": "^0.3.1", "@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
@ -1574,9 +1577,9 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.3.2", "debug": "^4.3.2",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.3.0", "eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.0", "eslint-visitor-keys": "^4.2.1",
"espree": "^10.3.0", "espree": "^10.4.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
"esutils": "^2.0.2", "esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
@ -1789,9 +1792,9 @@
} }
}, },
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "8.3.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"esrecurse": "^4.3.0", "esrecurse": "^4.3.0",
@ -1805,9 +1808,9 @@
} }
}, },
"node_modules/eslint-visitor-keys": { "node_modules/eslint-visitor-keys": {
"version": "4.2.0", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -1817,14 +1820,14 @@
} }
}, },
"node_modules/espree": { "node_modules/espree": {
"version": "10.3.0", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"acorn": "^8.14.0", "acorn": "^8.15.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.0" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -4032,15 +4035,15 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.33.0", "version": "8.34.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.33.0.tgz", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz",
"integrity": "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ==", "integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.33.0", "@typescript-eslint/eslint-plugin": "8.34.1",
"@typescript-eslint/parser": "8.33.0", "@typescript-eslint/parser": "8.34.1",
"@typescript-eslint/utils": "8.33.0" "@typescript-eslint/utils": "8.34.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View File

@ -1,6 +1,6 @@
[project] [project]
name = "authentik" name = "authentik"
version = "2025.6.0" version = "2025.6.2"
description = "" description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }] authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*" requires-python = "==3.13.*"
@ -9,11 +9,11 @@ dependencies = [
"celery==5.5.3", "celery==5.5.3",
"channels==4.2.2", "channels==4.2.2",
"channels-redis==4.2.1", "channels-redis==4.2.1",
"cryptography==45.0.3", "cryptography==45.0.4",
"dacite==1.9.2", "dacite==1.9.2",
"deepmerge==2.0", "deepmerge==2.0",
"defusedxml==0.7.1", "defusedxml==0.7.1",
"django==5.1.10", "django==5.1.11",
"django-countries==7.6.1", "django-countries==7.6.1",
"django-cte==1.3.3", "django-cte==1.3.3",
"django-filter==25.1", "django-filter==25.1",
@ -35,20 +35,20 @@ dependencies = [
"flower==2.0.1", "flower==2.0.1",
"geoip2==5.1.0", "geoip2==5.1.0",
"geopy==2.4.1", "geopy==2.4.1",
"google-api-python-client==2.171.0", "google-api-python-client==2.172.0",
"gssapi==1.9.0", "gssapi==1.9.0",
"gunicorn==23.0.0", "gunicorn==23.0.0",
"jsonpatch==1.33", "jsonpatch==1.33",
"jwcrypto==1.5.6", "jwcrypto==1.5.6",
"kubernetes==32.0.1", "kubernetes==33.1.0",
"ldap3==2.9.1", "ldap3==2.9.1",
"lxml==5.4.0", "lxml==5.4.0",
"msgraph-sdk==1.32.0", "msgraph-sdk==1.33.0",
"opencontainers==0.0.14", "opencontainers==0.0.14",
"packaging==25.0", "packaging==25.0",
"paramiko==3.5.1", "paramiko==3.5.1",
"psycopg[c,pool]==3.2.9", "psycopg[c,pool]==3.2.9",
"pydantic==2.11.5", "pydantic==2.11.7",
"pydantic-scim==0.0.8", "pydantic-scim==0.0.8",
"pyjwt==2.10.1", "pyjwt==2.10.1",
"pyrad==2.4", "pyrad==2.4",
@ -56,19 +56,19 @@ dependencies = [
"pyyaml==6.0.2", "pyyaml==6.0.2",
"requests-oauthlib==2.0.0", "requests-oauthlib==2.0.0",
"scim2-filter-parser==0.7.0", "scim2-filter-parser==0.7.0",
"sentry-sdk==2.29.1", "sentry-sdk==2.30.0",
"service-identity==24.2.0", "service-identity==24.2.0",
"setproctitle==1.3.6", "setproctitle==1.3.6",
"structlog==25.4.0", "structlog==25.4.0",
"swagger-spec-validator==3.0.4", "swagger-spec-validator==3.0.4",
"tenant-schemas-celery==3.0.0", "tenant-schemas-celery==3.0.0",
"twilio==9.6.2", "twilio==9.6.3",
"ua-parser==1.0.1", "ua-parser==1.0.1",
"unidecode==1.4.0", "unidecode==1.4.0",
"urllib3<3", "urllib3<3",
"uvicorn[standard]==0.34.3", "uvicorn[standard]==0.34.3",
"watchdog==6.0.0", "watchdog==6.0.0",
"webauthn==2.5.2", "webauthn==2.6.0",
"wsproto==1.2.0", "wsproto==1.2.0",
"xmlsec==1.3.15", "xmlsec==1.3.15",
"zxcvbn==4.5.0", "zxcvbn==4.5.0",
@ -140,6 +140,7 @@ skip = [
"**/storybook-static", "**/storybook-static",
"**/web/src/locales", "**/web/src/locales",
"**/web/xliff", "**/web/xliff",
"**/web/out",
"./web/storybook-static", "./web/storybook-static",
"./web/custom-elements.json", "./web/custom-elements.json",
"./website/build", "./website/build",

View File

@ -1,7 +1,7 @@
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: authentik title: authentik
version: 2025.6.0 version: 2025.6.2
description: Making authentication simple. description: Making authentication simple.
contact: contact:
email: hello@goauthentik.io email: hello@goauthentik.io
@ -38,33 +38,6 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' description: ''
/admin/metrics/:
get:
operationId: admin_metrics_retrieve
description: Login Metrics per 1h
tags:
- admin
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/LoginMetrics'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/admin/models/: /admin/models/:
get: get:
operationId: admin_models_list operationId: admin_models_list
@ -4136,42 +4109,6 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' description: ''
/core/applications/{slug}/metrics/:
get:
operationId: core_applications_metrics_list
description: Metrics for application logins
parameters:
- in: path
name: slug
schema:
type: string
description: Internal application name, used in URLs.
required: true
tags:
- core
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Coordinate'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/core/applications/{slug}/set_icon/: /core/applications/{slug}/set_icon/:
post: post:
operationId: core_applications_set_icon_create operationId: core_applications_set_icon_create
@ -6071,40 +6008,6 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' description: ''
/core/users/{id}/metrics/:
get:
operationId: core_users_metrics_retrieve
description: User metrics per 1h
parameters:
- in: path
name: id
schema:
type: integer
description: A unique integer value identifying this User.
required: true
tags:
- core
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/UserMetrics'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/core/users/{id}/recovery/: /core/users/{id}/recovery/:
post: post:
operationId: core_users_recovery_create operationId: core_users_recovery_create
@ -7112,6 +7015,42 @@ paths:
name: action name: action
schema: schema:
type: string type: string
- in: query
name: actions
schema:
type: array
items:
type: string
enum:
- authorize_application
- configuration_error
- custom_
- email_sent
- flow_execution
- impersonation_ended
- impersonation_started
- invitation_used
- login
- login_failed
- logout
- model_created
- model_deleted
- model_updated
- password_set
- policy_exception
- policy_execution
- property_mapping_exception
- secret_rotate
- secret_view
- source_linked
- suspicious_request
- system_exception
- system_task_exception
- system_task_execution
- update_available
- user_write
explode: true
style: form
- in: query - in: query
name: brand_name name: brand_name
schema: schema:
@ -7398,44 +7337,6 @@ paths:
schema: schema:
$ref: '#/components/schemas/GenericError' $ref: '#/components/schemas/GenericError'
description: '' description: ''
/events/events/per_month/:
get:
operationId: events_events_per_month_list
description: Get the count of events per month
parameters:
- in: query
name: action
schema:
type: string
- in: query
name: query
schema:
type: string
tags:
- events
security:
- authentik: []
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Coordinate'
description: ''
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/GenericError'
description: ''
/events/events/top_per_user/: /events/events/top_per_user/:
get: get:
operationId: events_events_top_per_user_list operationId: events_events_top_per_user_list
@ -7483,6 +7384,42 @@ paths:
name: action name: action
schema: schema:
type: string type: string
- in: query
name: actions
schema:
type: array
items:
type: string
enum:
- authorize_application
- configuration_error
- custom_
- email_sent
- flow_execution
- impersonation_ended
- impersonation_started
- invitation_used
- login
- login_failed
- logout
- model_created
- model_deleted
- model_updated
- password_set
- policy_exception
- policy_execution
- property_mapping_exception
- secret_rotate
- secret_view
- source_linked
- suspicious_request
- system_exception
- system_task_exception
- system_task_execution
- update_available
- user_write
explode: true
style: form
- in: query - in: query
name: brand_name name: brand_name
schema: schema:
@ -7512,6 +7449,11 @@ paths:
schema: schema:
type: string type: string
description: Context Model Primary Key description: Context Model Primary Key
- in: query
name: history_days
schema:
type: number
default: 7
- name: ordering - name: ordering
required: false required: false
in: query in: query
@ -7540,7 +7482,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/Coordinate' $ref: '#/components/schemas/EventVolume'
description: '' description: ''
'400': '400':
content: content:
@ -22512,6 +22454,17 @@ paths:
schema: schema:
type: string type: string
format: uuid format: uuid
- in: query
name: default_name_id_policy
schema:
type: string
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- in: query - in: query
name: default_relay_state name: default_relay_state
schema: schema:
@ -29728,6 +29681,7 @@ paths:
enum: enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName - urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName - urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent - urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient - urn:oasis:names:tc:SAML:2.0:nameid-format:transient
@ -43596,19 +43550,6 @@ components:
- sidebar_left - sidebar_left
- sidebar_right - sidebar_right
type: string type: string
Coordinate:
type: object
description: Coordinates for diagrams
properties:
x_cord:
type: integer
readOnly: true
y_cord:
type: integer
readOnly: true
required:
- x_cord
- y_cord
CountryCodeEnum: CountryCodeEnum:
enum: enum:
- AF - AF
@ -44986,6 +44927,21 @@ components:
- application - application
- counted_events - counted_events
- unique_users - unique_users
EventVolume:
type: object
description: Count of events of action created on day
properties:
action:
$ref: '#/components/schemas/EventActions'
time:
type: string
format: date-time
count:
type: integer
required:
- action
- count
- time
EventsRequestedEnum: EventsRequestedEnum:
enum: enum:
- https://schemas.openid.net/secevent/caep/event-type/session-revoked - https://schemas.openid.net/secevent/caep/event-type/session-revoked
@ -48297,29 +48253,6 @@ components:
xak-flow-redirect: '#/components/schemas/RedirectChallenge' xak-flow-redirect: '#/components/schemas/RedirectChallenge'
ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge' ak-source-oauth-apple: '#/components/schemas/AppleLoginChallenge'
ak-source-plex: '#/components/schemas/PlexAuthenticationChallenge' ak-source-plex: '#/components/schemas/PlexAuthenticationChallenge'
LoginMetrics:
type: object
description: Login Metrics per 1h
properties:
logins:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
logins_failed:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
authorizations:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
required:
- authorizations
- logins
- logins_failed
LoginSource: LoginSource:
type: object type: object
description: Serializer for Login buttons of sources description: Serializer for Login buttons of sources
@ -48824,14 +48757,6 @@ components:
- mode - mode
- name - name
- user_attribute - user_attribute
NameIdPolicyEnum:
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
type: string
NetworkBindingEnum: NetworkBindingEnum:
enum: enum:
- no_binding - no_binding
@ -54580,6 +54505,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
PatchedSAMLSourcePropertyMappingRequest: PatchedSAMLSourcePropertyMappingRequest:
type: object type: object
description: SAMLSourcePropertyMapping Serializer description: SAMLSourcePropertyMapping Serializer
@ -54673,7 +54600,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:
@ -57384,6 +57311,15 @@ components:
required: required:
- download_url - download_url
- metadata - metadata
SAMLNameIDPolicyEnum:
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
- urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
- urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
type: string
SAMLPropertyMapping: SAMLPropertyMapping:
type: object type: object
description: SAMLPropertyMapping Serializer description: SAMLPropertyMapping Serializer
@ -57601,6 +57537,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
url_download_metadata: url_download_metadata:
type: string type: string
description: Get metadata download URL description: Get metadata download URL
@ -57773,6 +57711,8 @@ components:
default_relay_state: default_relay_state:
type: string type: string
description: Default relay_state value for IDP-initiated logins description: Default relay_state value for IDP-initiated logins
default_name_id_policy:
$ref: '#/components/schemas/SAMLNameIDPolicyEnum'
required: required:
- acs_url - acs_url
- authorization_flow - authorization_flow
@ -57881,7 +57821,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:
@ -58071,7 +58011,7 @@ components:
be a security risk, as no validation of the request ID is done. be a security risk, as no validation of the request ID is done.
name_id_policy: name_id_policy:
allOf: allOf:
- $ref: '#/components/schemas/NameIdPolicyEnum' - $ref: '#/components/schemas/SAMLNameIDPolicyEnum'
description: NameID Policy sent to the IdP. Can be unset, in which case description: NameID Policy sent to the IdP. Can be unset, in which case
no Policy is sent. no Policy is sent.
binding_type: binding_type:
@ -60729,29 +60669,6 @@ components:
- username_link - username_link
- username_deny - username_deny
type: string type: string
UserMetrics:
type: object
description: User Metrics
properties:
logins:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
logins_failed:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
authorizations:
type: array
items:
$ref: '#/components/schemas/Coordinate'
readOnly: true
required:
- authorizations
- logins
- logins_failed
UserOAuthSourceConnection: UserOAuthSourceConnection:
type: object type: object
description: User source connection description: User source connection

View File

@ -1,13 +1,13 @@
services: services:
chrome: chrome:
platform: linux/x86_64 platform: linux/x86_64
image: docker.io/selenium/standalone-chrome:136.0 image: docker.io/selenium/standalone-chrome:137.0
volumes: volumes:
- /dev/shm:/dev/shm - /dev/shm:/dev/shm
network_mode: host network_mode: host
restart: always restart: always
mailpit: mailpit:
image: docker.io/axllent/mailpit:v1.25.1 image: docker.io/axllent/mailpit:v1.26.1
ports: ports:
- 1025:1025 - 1025:1025
- 8025:8025 - 8025:8025

View File

@ -2,7 +2,6 @@
from dataclasses import asdict from dataclasses import asdict
from time import sleep from time import sleep
from unittest.mock import patch
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server
@ -16,12 +15,10 @@ from authentik.flows.models import Flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.outposts.apps import MANAGED_OUTPOST from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.ldap.models import APIAccessMode, LDAPProvider from authentik.providers.ldap.models import APIAccessMode, LDAPProvider
from tests.e2e.utils import SeleniumTestCase, retry from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderLDAP(SeleniumTestCase): class TestProviderLDAP(SeleniumTestCase):
"""LDAP and Outpost e2e tests""" """LDAP and Outpost e2e tests"""

View File

@ -6,7 +6,6 @@ from json import loads
from sys import platform from sys import platform
from time import sleep from time import sleep
from unittest.case import skip, skipUnless from unittest.case import skip, skipUnless
from unittest.mock import patch
from channels.testing import ChannelsLiveServerTestCase from channels.testing import ChannelsLiveServerTestCase
from jwt import decode from jwt import decode
@ -18,12 +17,10 @@ from authentik.flows.models import Flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostConfig, OutpostType
from authentik.outposts.tasks import outpost_connection_discovery from authentik.outposts.tasks import outpost_connection_discovery
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.proxy.models import ProxyProvider from authentik.providers.proxy.models import ProxyProvider
from tests.e2e.utils import SeleniumTestCase, retry from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderProxy(SeleniumTestCase): class TestProviderProxy(SeleniumTestCase):
"""Proxy and Outpost e2e tests""" """Proxy and Outpost e2e tests"""

View File

@ -4,7 +4,6 @@ from json import loads
from pathlib import Path from pathlib import Path
from time import sleep from time import sleep
from unittest import skip from unittest import skip
from unittest.mock import patch
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
@ -13,12 +12,10 @@ from authentik.core.models import Application
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.outposts.models import Outpost, OutpostType from authentik.outposts.models import Outpost, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.proxy.models import ProxyMode, ProxyProvider from authentik.providers.proxy.models import ProxyMode, ProxyProvider
from tests.e2e.utils import SeleniumTestCase, retry from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderProxyForward(SeleniumTestCase): class TestProviderProxyForward(SeleniumTestCase):
"""Proxy and Outpost e2e tests""" """Proxy and Outpost e2e tests"""

View File

@ -2,7 +2,6 @@
from dataclasses import asdict from dataclasses import asdict
from time import sleep from time import sleep
from unittest.mock import patch
from pyrad.client import Client from pyrad.client import Client
from pyrad.dictionary import Dictionary from pyrad.dictionary import Dictionary
@ -13,12 +12,10 @@ from authentik.core.models import Application, User
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.generators import generate_id, generate_key from authentik.lib.generators import generate_id, generate_key
from authentik.outposts.models import Outpost, OutpostConfig, OutpostType from authentik.outposts.models import Outpost, OutpostConfig, OutpostType
from authentik.outposts.tests.test_ws import patched__get_ct_cached
from authentik.providers.radius.models import RadiusProvider from authentik.providers.radius.models import RadiusProvider
from tests.e2e.utils import SeleniumTestCase, retry from tests.e2e.utils import SeleniumTestCase, retry
@patch("guardian.shortcuts._get_ct_cached", patched__get_ct_cached)
class TestProviderRadius(SeleniumTestCase): class TestProviderRadius(SeleniumTestCase):
"""Radius Outpost e2e tests""" """Radius Outpost e2e tests"""

View File

@ -1,7 +1,6 @@
"""authentik e2e testing utilities""" """authentik e2e testing utilities"""
import json import json
import os
import socket import socket
from collections.abc import Callable from collections.abc import Callable
from functools import lru_cache, wraps from functools import lru_cache, wraps
@ -37,22 +36,12 @@ from authentik.core.api.users import UserSerializer
from authentik.core.models import User from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.root.test_runner import get_docker_tag
IS_CI = "CI" in environ IS_CI = "CI" in environ
RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1 RETRIES = int(environ.get("RETRIES", "3")) if IS_CI else 1
def get_docker_tag() -> str:
"""Get docker-tag based off of CI variables"""
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
branch_name = os.environ.get(default_branch, "main")
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
return f"gh-{branch_name}"
def get_local_ip() -> str: def get_local_ip() -> str:
"""Get the local machine's IP""" """Get the local machine's IP"""
hostname = socket.gethostname() hostname = socket.gethostname()

121
uv.lock generated
View File

@ -165,7 +165,7 @@ wheels = [
[[package]] [[package]]
name = "authentik" name = "authentik"
version = "2025.6.0" version = "2025.6.2"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "argon2-cffi" }, { name = "argon2-cffi" },
@ -270,11 +270,11 @@ requires-dist = [
{ name = "celery", specifier = "==5.5.3" }, { name = "celery", specifier = "==5.5.3" },
{ name = "channels", specifier = "==4.2.2" }, { name = "channels", specifier = "==4.2.2" },
{ name = "channels-redis", specifier = "==4.2.1" }, { name = "channels-redis", specifier = "==4.2.1" },
{ name = "cryptography", specifier = "==45.0.3" }, { name = "cryptography", specifier = "==45.0.4" },
{ name = "dacite", specifier = "==1.9.2" }, { name = "dacite", specifier = "==1.9.2" },
{ name = "deepmerge", specifier = "==2.0" }, { name = "deepmerge", specifier = "==2.0" },
{ name = "defusedxml", specifier = "==0.7.1" }, { name = "defusedxml", specifier = "==0.7.1" },
{ name = "django", specifier = "==5.1.10" }, { name = "django", specifier = "==5.1.11" },
{ name = "django-countries", specifier = "==7.6.1" }, { name = "django-countries", specifier = "==7.6.1" },
{ name = "django-cte", specifier = "==1.3.3" }, { name = "django-cte", specifier = "==1.3.3" },
{ name = "django-filter", specifier = "==25.1" }, { name = "django-filter", specifier = "==25.1" },
@ -296,20 +296,20 @@ requires-dist = [
{ name = "flower", specifier = "==2.0.1" }, { name = "flower", specifier = "==2.0.1" },
{ name = "geoip2", specifier = "==5.1.0" }, { name = "geoip2", specifier = "==5.1.0" },
{ name = "geopy", specifier = "==2.4.1" }, { name = "geopy", specifier = "==2.4.1" },
{ name = "google-api-python-client", specifier = "==2.171.0" }, { name = "google-api-python-client", specifier = "==2.172.0" },
{ name = "gssapi", specifier = "==1.9.0" }, { name = "gssapi", specifier = "==1.9.0" },
{ name = "gunicorn", specifier = "==23.0.0" }, { name = "gunicorn", specifier = "==23.0.0" },
{ name = "jsonpatch", specifier = "==1.33" }, { name = "jsonpatch", specifier = "==1.33" },
{ name = "jwcrypto", specifier = "==1.5.6" }, { name = "jwcrypto", specifier = "==1.5.6" },
{ name = "kubernetes", specifier = "==32.0.1" }, { name = "kubernetes", specifier = "==33.1.0" },
{ name = "ldap3", specifier = "==2.9.1" }, { name = "ldap3", specifier = "==2.9.1" },
{ name = "lxml", specifier = "==5.4.0" }, { name = "lxml", specifier = "==5.4.0" },
{ name = "msgraph-sdk", specifier = "==1.32.0" }, { name = "msgraph-sdk", specifier = "==1.33.0" },
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" }, { name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=ceb4fcc090851717a3069d78e85ceb1e86c2740c" },
{ name = "packaging", specifier = "==25.0" }, { name = "packaging", specifier = "==25.0" },
{ name = "paramiko", specifier = "==3.5.1" }, { name = "paramiko", specifier = "==3.5.1" },
{ name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" }, { name = "psycopg", extras = ["c", "pool"], specifier = "==3.2.9" },
{ name = "pydantic", specifier = "==2.11.5" }, { name = "pydantic", specifier = "==2.11.7" },
{ name = "pydantic-scim", specifier = "==0.0.8" }, { name = "pydantic-scim", specifier = "==0.0.8" },
{ name = "pyjwt", specifier = "==2.10.1" }, { name = "pyjwt", specifier = "==2.10.1" },
{ name = "pyrad", specifier = "==2.4" }, { name = "pyrad", specifier = "==2.4" },
@ -317,19 +317,19 @@ requires-dist = [
{ name = "pyyaml", specifier = "==6.0.2" }, { name = "pyyaml", specifier = "==6.0.2" },
{ name = "requests-oauthlib", specifier = "==2.0.0" }, { name = "requests-oauthlib", specifier = "==2.0.0" },
{ name = "scim2-filter-parser", specifier = "==0.7.0" }, { name = "scim2-filter-parser", specifier = "==0.7.0" },
{ name = "sentry-sdk", specifier = "==2.29.1" }, { name = "sentry-sdk", specifier = "==2.30.0" },
{ name = "service-identity", specifier = "==24.2.0" }, { name = "service-identity", specifier = "==24.2.0" },
{ name = "setproctitle", specifier = "==1.3.6" }, { name = "setproctitle", specifier = "==1.3.6" },
{ name = "structlog", specifier = "==25.4.0" }, { name = "structlog", specifier = "==25.4.0" },
{ name = "swagger-spec-validator", specifier = "==3.0.4" }, { name = "swagger-spec-validator", specifier = "==3.0.4" },
{ name = "tenant-schemas-celery", specifier = "==3.0.0" }, { name = "tenant-schemas-celery", specifier = "==3.0.0" },
{ name = "twilio", specifier = "==9.6.2" }, { name = "twilio", specifier = "==9.6.3" },
{ name = "ua-parser", specifier = "==1.0.1" }, { name = "ua-parser", specifier = "==1.0.1" },
{ name = "unidecode", specifier = "==1.4.0" }, { name = "unidecode", specifier = "==1.4.0" },
{ name = "urllib3", specifier = "<3" }, { name = "urllib3", specifier = "<3" },
{ name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" }, { name = "uvicorn", extras = ["standard"], specifier = "==0.34.3" },
{ name = "watchdog", specifier = "==6.0.0" }, { name = "watchdog", specifier = "==6.0.0" },
{ name = "webauthn", specifier = "==2.5.2" }, { name = "webauthn", specifier = "==2.6.0" },
{ name = "wsproto", specifier = "==1.2.0" }, { name = "wsproto", specifier = "==1.2.0" },
{ name = "xmlsec", specifier = "==1.3.15" }, { name = "xmlsec", specifier = "==1.3.15" },
{ name = "zxcvbn", specifier = "==4.5.0" }, { name = "zxcvbn", specifier = "==4.5.0" },
@ -870,37 +870,37 @@ wheels = [
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "45.0.3" version = "45.0.4"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/13/1f/9fa001e74a1993a9cadd2333bb889e50c66327b8594ac538ab8a04f915b7/cryptography-45.0.3.tar.gz", hash = "sha256:ec21313dd335c51d7877baf2972569f40a4291b76a0ce51391523ae358d05899", size = 744738, upload-time = "2025-05-25T14:17:24.777Z" } sdist = { url = "https://files.pythonhosted.org/packages/fe/c8/a2a376a8711c1e11708b9c9972e0c3223f5fc682552c82d8db844393d6ce/cryptography-45.0.4.tar.gz", hash = "sha256:7405ade85c83c37682c8fe65554759800a4a8c54b2d96e0f8ad114d31b808d57", size = 744890, upload-time = "2025-06-10T00:03:51.297Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/82/b2/2345dc595998caa6f68adf84e8f8b50d18e9fc4638d32b22ea8daedd4b7a/cryptography-45.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:7573d9eebaeceeb55285205dbbb8753ac1e962af3d9640791d12b36864065e71", size = 7056239, upload-time = "2025-05-25T14:16:12.22Z" }, { url = "https://files.pythonhosted.org/packages/cc/1c/92637793de053832523b410dbe016d3f5c11b41d0cf6eef8787aabb51d41/cryptography-45.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:425a9a6ac2823ee6e46a76a21a4e8342d8fa5c01e08b823c1f19a8b74f096069", size = 7055712, upload-time = "2025-06-10T00:02:38.826Z" },
{ url = "https://files.pythonhosted.org/packages/71/3d/ac361649a0bfffc105e2298b720d8b862330a767dab27c06adc2ddbef96a/cryptography-45.0.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d377dde61c5d67eb4311eace661c3efda46c62113ff56bf05e2d679e02aebb5b", size = 4205541, upload-time = "2025-05-25T14:16:14.333Z" }, { url = "https://files.pythonhosted.org/packages/ba/14/93b69f2af9ba832ad6618a03f8a034a5851dc9a3314336a3d71c252467e1/cryptography-45.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:680806cf63baa0039b920f4976f5f31b10e772de42f16310a6839d9f21a26b0d", size = 4205335, upload-time = "2025-06-10T00:02:41.64Z" },
{ url = "https://files.pythonhosted.org/packages/70/3e/c02a043750494d5c445f769e9c9f67e550d65060e0bfce52d91c1362693d/cryptography-45.0.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fae1e637f527750811588e4582988932c222f8251f7b7ea93739acb624e1487f", size = 4433275, upload-time = "2025-05-25T14:16:16.421Z" }, { url = "https://files.pythonhosted.org/packages/67/30/fae1000228634bf0b647fca80403db5ca9e3933b91dd060570689f0bd0f7/cryptography-45.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ca0f52170e821bc8da6fc0cc565b7bb8ff8d90d36b5e9fdd68e8a86bdf72036", size = 4431487, upload-time = "2025-06-10T00:02:43.696Z" },
{ url = "https://files.pythonhosted.org/packages/40/7a/9af0bfd48784e80eef3eb6fd6fde96fe706b4fc156751ce1b2b965dada70/cryptography-45.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ca932e11218bcc9ef812aa497cdf669484870ecbcf2d99b765d6c27a86000942", size = 4209173, upload-time = "2025-05-25T14:16:18.163Z" }, { url = "https://files.pythonhosted.org/packages/6d/5a/7dffcf8cdf0cb3c2430de7404b327e3db64735747d641fc492539978caeb/cryptography-45.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f3fe7a5ae34d5a414957cc7f457e2b92076e72938423ac64d215722f6cf49a9e", size = 4208922, upload-time = "2025-06-10T00:02:45.334Z" },
{ url = "https://files.pythonhosted.org/packages/31/5f/d6f8753c8708912df52e67969e80ef70b8e8897306cd9eb8b98201f8c184/cryptography-45.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af3f92b1dc25621f5fad065288a44ac790c5798e986a34d393ab27d2b27fcff9", size = 3898150, upload-time = "2025-05-25T14:16:20.34Z" }, { url = "https://files.pythonhosted.org/packages/c6/f3/528729726eb6c3060fa3637253430547fbaaea95ab0535ea41baa4a6fbd8/cryptography-45.0.4-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:25eb4d4d3e54595dc8adebc6bbd5623588991d86591a78c2548ffb64797341e2", size = 3900433, upload-time = "2025-06-10T00:02:47.359Z" },
{ url = "https://files.pythonhosted.org/packages/8b/50/f256ab79c671fb066e47336706dc398c3b1e125f952e07d54ce82cf4011a/cryptography-45.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f8f8f0b73b885ddd7f3d8c2b2234a7d3ba49002b0223f58cfde1bedd9563c56", size = 4466473, upload-time = "2025-05-25T14:16:22.605Z" }, { url = "https://files.pythonhosted.org/packages/d9/4a/67ba2e40f619e04d83c32f7e1d484c1538c0800a17c56a22ff07d092ccc1/cryptography-45.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ce1678a2ccbe696cf3af15a75bb72ee008d7ff183c9228592ede9db467e64f1b", size = 4464163, upload-time = "2025-06-10T00:02:49.412Z" },
{ url = "https://files.pythonhosted.org/packages/62/e7/312428336bb2df0848d0768ab5a062e11a32d18139447a76dfc19ada8eed/cryptography-45.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9cc80ce69032ffa528b5e16d217fa4d8d4bb7d6ba8659c1b4d74a1b0f4235fca", size = 4211890, upload-time = "2025-05-25T14:16:24.738Z" }, { url = "https://files.pythonhosted.org/packages/7e/9a/b4d5aa83661483ac372464809c4b49b5022dbfe36b12fe9e323ca8512420/cryptography-45.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:49fe9155ab32721b9122975e168a6760d8ce4cffe423bcd7ca269ba41b5dfac1", size = 4208687, upload-time = "2025-06-10T00:02:50.976Z" },
{ url = "https://files.pythonhosted.org/packages/e7/53/8a130e22c1e432b3c14896ec5eb7ac01fb53c6737e1d705df7e0efb647c6/cryptography-45.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c824c9281cb628015bfc3c59335163d4ca0540d49de4582d6c2637312907e4b1", size = 4466300, upload-time = "2025-05-25T14:16:26.768Z" }, { url = "https://files.pythonhosted.org/packages/db/b7/a84bdcd19d9c02ec5807f2ec2d1456fd8451592c5ee353816c09250e3561/cryptography-45.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2882338b2a6e0bd337052e8b9007ced85c637da19ef9ecaf437744495c8c2999", size = 4463623, upload-time = "2025-06-10T00:02:52.542Z" },
{ url = "https://files.pythonhosted.org/packages/ba/75/6bb6579688ef805fd16a053005fce93944cdade465fc92ef32bbc5c40681/cryptography-45.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5833bb4355cb377ebd880457663a972cd044e7f49585aee39245c0d592904578", size = 4332483, upload-time = "2025-05-25T14:16:28.316Z" }, { url = "https://files.pythonhosted.org/packages/d8/84/69707d502d4d905021cac3fb59a316344e9f078b1da7fb43ecde5e10840a/cryptography-45.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:23b9c3ea30c3ed4db59e7b9619272e94891f8a3a5591d0b656a7582631ccf750", size = 4332447, upload-time = "2025-06-10T00:02:54.63Z" },
{ url = "https://files.pythonhosted.org/packages/2f/11/2538f4e1ce05c6c4f81f43c1ef2bd6de7ae5e24ee284460ff6c77e42ca77/cryptography-45.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bb5bf55dcb69f7067d80354d0a348368da907345a2c448b0babc4215ccd3497", size = 4573714, upload-time = "2025-05-25T14:16:30.474Z" }, { url = "https://files.pythonhosted.org/packages/f3/ee/d4f2ab688e057e90ded24384e34838086a9b09963389a5ba6854b5876598/cryptography-45.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0a97c927497e3bc36b33987abb99bf17a9a175a19af38a892dc4bbb844d7ee2", size = 4572830, upload-time = "2025-06-10T00:02:56.689Z" },
{ url = "https://files.pythonhosted.org/packages/f5/bb/e86e9cf07f73a98d84a4084e8fd420b0e82330a901d9cac8149f994c3417/cryptography-45.0.3-cp311-abi3-win32.whl", hash = "sha256:3ad69eeb92a9de9421e1f6685e85a10fbcfb75c833b42cc9bc2ba9fb00da4710", size = 2934752, upload-time = "2025-05-25T14:16:32.204Z" }, { url = "https://files.pythonhosted.org/packages/70/d4/994773a261d7ff98034f72c0e8251fe2755eac45e2265db4c866c1c6829c/cryptography-45.0.4-cp311-abi3-win32.whl", hash = "sha256:e00a6c10a5c53979d6242f123c0a97cff9f3abed7f064fc412c36dc521b5f257", size = 2932769, upload-time = "2025-06-10T00:02:58.467Z" },
{ url = "https://files.pythonhosted.org/packages/c7/75/063bc9ddc3d1c73e959054f1fc091b79572e716ef74d6caaa56e945b4af9/cryptography-45.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:97787952246a77d77934d41b62fb1b6f3581d83f71b44796a4158d93b8f5c490", size = 3412465, upload-time = "2025-05-25T14:16:33.888Z" }, { url = "https://files.pythonhosted.org/packages/5a/42/c80bd0b67e9b769b364963b5252b17778a397cefdd36fa9aa4a5f34c599a/cryptography-45.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:817ee05c6c9f7a69a16200f0c90ab26d23a87701e2a284bd15156783e46dbcc8", size = 3410441, upload-time = "2025-06-10T00:03:00.14Z" },
{ url = "https://files.pythonhosted.org/packages/71/9b/04ead6015229a9396890d7654ee35ef630860fb42dc9ff9ec27f72157952/cryptography-45.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:c92519d242703b675ccefd0f0562eb45e74d438e001f8ab52d628e885751fb06", size = 7031892, upload-time = "2025-05-25T14:16:36.214Z" }, { url = "https://files.pythonhosted.org/packages/ce/0b/2488c89f3a30bc821c9d96eeacfcab6ff3accc08a9601ba03339c0fd05e5/cryptography-45.0.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:964bcc28d867e0f5491a564b7debb3ffdd8717928d315d12e0d7defa9e43b723", size = 7031836, upload-time = "2025-06-10T00:03:01.726Z" },
{ url = "https://files.pythonhosted.org/packages/46/c7/c7d05d0e133a09fc677b8a87953815c522697bdf025e5cac13ba419e7240/cryptography-45.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5edcb90da1843df85292ef3a313513766a78fbbb83f584a5a58fb001a5a9d57", size = 4196181, upload-time = "2025-05-25T14:16:37.934Z" }, { url = "https://files.pythonhosted.org/packages/fe/51/8c584ed426093aac257462ae62d26ad61ef1cbf5b58d8b67e6e13c39960e/cryptography-45.0.4-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6a5bf57554e80f75a7db3d4b1dacaa2764611ae166ab42ea9a72bcdb5d577637", size = 4195746, upload-time = "2025-06-10T00:03:03.94Z" },
{ url = "https://files.pythonhosted.org/packages/08/7a/6ad3aa796b18a683657cef930a986fac0045417e2dc428fd336cfc45ba52/cryptography-45.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38deed72285c7ed699864f964a3f4cf11ab3fb38e8d39cfcd96710cd2b5bb716", size = 4423370, upload-time = "2025-05-25T14:16:39.502Z" }, { url = "https://files.pythonhosted.org/packages/5c/7d/4b0ca4d7af95a704eef2f8f80a8199ed236aaf185d55385ae1d1610c03c2/cryptography-45.0.4-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:46cf7088bf91bdc9b26f9c55636492c1cce3e7aaf8041bbf0243f5e5325cfb2d", size = 4424456, upload-time = "2025-06-10T00:03:05.589Z" },
{ url = "https://files.pythonhosted.org/packages/4f/58/ec1461bfcb393525f597ac6a10a63938d18775b7803324072974b41a926b/cryptography-45.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5555365a50efe1f486eed6ac7062c33b97ccef409f5970a0b6f205a7cfab59c8", size = 4197839, upload-time = "2025-05-25T14:16:41.322Z" }, { url = "https://files.pythonhosted.org/packages/1d/45/5fabacbc6e76ff056f84d9f60eeac18819badf0cefc1b6612ee03d4ab678/cryptography-45.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7bedbe4cc930fa4b100fc845ea1ea5788fcd7ae9562e669989c11618ae8d76ee", size = 4198495, upload-time = "2025-06-10T00:03:09.172Z" },
{ url = "https://files.pythonhosted.org/packages/d4/3d/5185b117c32ad4f40846f579369a80e710d6146c2baa8ce09d01612750db/cryptography-45.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e4253ed8f5948a3589b3caee7ad9a5bf218ffd16869c516535325fece163dcc", size = 3886324, upload-time = "2025-05-25T14:16:43.041Z" }, { url = "https://files.pythonhosted.org/packages/55/b7/ffc9945b290eb0a5d4dab9b7636706e3b5b92f14ee5d9d4449409d010d54/cryptography-45.0.4-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eaa3e28ea2235b33220b949c5a0d6cf79baa80eab2eb5607ca8ab7525331b9ff", size = 3885540, upload-time = "2025-06-10T00:03:10.835Z" },
{ url = "https://files.pythonhosted.org/packages/67/85/caba91a57d291a2ad46e74016d1f83ac294f08128b26e2a81e9b4f2d2555/cryptography-45.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cfd84777b4b6684955ce86156cfb5e08d75e80dc2585e10d69e47f014f0a5342", size = 4450447, upload-time = "2025-05-25T14:16:44.759Z" }, { url = "https://files.pythonhosted.org/packages/7f/e3/57b010282346980475e77d414080acdcb3dab9a0be63071efc2041a2c6bd/cryptography-45.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:7ef2dde4fa9408475038fc9aadfc1fb2676b174e68356359632e980c661ec8f6", size = 4452052, upload-time = "2025-06-10T00:03:12.448Z" },
{ url = "https://files.pythonhosted.org/packages/ae/d1/164e3c9d559133a38279215c712b8ba38e77735d3412f37711b9f8f6f7e0/cryptography-45.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a2b56de3417fd5f48773ad8e91abaa700b678dc7fe1e0c757e1ae340779acf7b", size = 4200576, upload-time = "2025-05-25T14:16:46.438Z" }, { url = "https://files.pythonhosted.org/packages/37/e6/ddc4ac2558bf2ef517a358df26f45bc774a99bf4653e7ee34b5e749c03e3/cryptography-45.0.4-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6a3511ae33f09094185d111160fd192c67aa0a2a8d19b54d36e4c78f651dc5ad", size = 4198024, upload-time = "2025-06-10T00:03:13.976Z" },
{ url = "https://files.pythonhosted.org/packages/71/7a/e002d5ce624ed46dfc32abe1deff32190f3ac47ede911789ee936f5a4255/cryptography-45.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:57a6500d459e8035e813bd8b51b671977fb149a8c95ed814989da682314d0782", size = 4450308, upload-time = "2025-05-25T14:16:48.228Z" }, { url = "https://files.pythonhosted.org/packages/3a/c0/85fa358ddb063ec588aed4a6ea1df57dc3e3bc1712d87c8fa162d02a65fc/cryptography-45.0.4-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:06509dc70dd71fa56eaa138336244e2fbaf2ac164fc9b5e66828fccfd2b680d6", size = 4451442, upload-time = "2025-06-10T00:03:16.248Z" },
{ url = "https://files.pythonhosted.org/packages/87/ad/3fbff9c28cf09b0a71e98af57d74f3662dea4a174b12acc493de00ea3f28/cryptography-45.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f22af3c78abfbc7cbcdf2c55d23c3e022e1a462ee2481011d518c7fb9c9f3d65", size = 4325125, upload-time = "2025-05-25T14:16:49.844Z" }, { url = "https://files.pythonhosted.org/packages/33/67/362d6ec1492596e73da24e669a7fbbaeb1c428d6bf49a29f7a12acffd5dc/cryptography-45.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5f31e6b0a5a253f6aa49be67279be4a7e5a4ef259a9f33c69f7d1b1191939872", size = 4325038, upload-time = "2025-06-10T00:03:18.4Z" },
{ url = "https://files.pythonhosted.org/packages/f5/b4/51417d0cc01802304c1984d76e9592f15e4801abd44ef7ba657060520bf0/cryptography-45.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:232954730c362638544758a8160c4ee1b832dc011d2c41a306ad8f7cccc5bb0b", size = 4560038, upload-time = "2025-05-25T14:16:51.398Z" }, { url = "https://files.pythonhosted.org/packages/53/75/82a14bf047a96a1b13ebb47fb9811c4f73096cfa2e2b17c86879687f9027/cryptography-45.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:944e9ccf67a9594137f942d5b52c8d238b1b4e46c7a0c2891b7ae6e01e7c80a4", size = 4560964, upload-time = "2025-06-10T00:03:20.06Z" },
{ url = "https://files.pythonhosted.org/packages/80/38/d572f6482d45789a7202fb87d052deb7a7b136bf17473ebff33536727a2c/cryptography-45.0.3-cp37-abi3-win32.whl", hash = "sha256:cb6ab89421bc90e0422aca911c69044c2912fc3debb19bb3c1bfe28ee3dff6ab", size = 2924070, upload-time = "2025-05-25T14:16:53.472Z" }, { url = "https://files.pythonhosted.org/packages/cd/37/1a3cba4c5a468ebf9b95523a5ef5651244693dc712001e276682c278fc00/cryptography-45.0.4-cp37-abi3-win32.whl", hash = "sha256:c22fe01e53dc65edd1945a2e6f0015e887f84ced233acecb64b4daadb32f5c97", size = 2924557, upload-time = "2025-06-10T00:03:22.563Z" },
{ url = "https://files.pythonhosted.org/packages/91/5a/61f39c0ff4443651cc64e626fa97ad3099249152039952be8f344d6b0c86/cryptography-45.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:d54ae41e6bd70ea23707843021c778f151ca258081586f0cfa31d936ae43d1b2", size = 3395005, upload-time = "2025-05-25T14:16:55.134Z" }, { url = "https://files.pythonhosted.org/packages/2a/4b/3256759723b7e66380397d958ca07c59cfc3fb5c794fb5516758afd05d41/cryptography-45.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:627ba1bc94f6adf0b0a2e35d87020285ead22d9f648c7e75bb64f367375f3b22", size = 3395508, upload-time = "2025-06-10T00:03:24.586Z" },
] ]
[[package]] [[package]]
@ -968,16 +968,16 @@ wheels = [
[[package]] [[package]]
name = "django" name = "django"
version = "5.1.10" version = "5.1.11"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "asgiref" }, { name = "asgiref" },
{ name = "sqlparse" }, { name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "tzdata", marker = "sys_platform == 'win32'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/73/ca/1c724be89e603eb8b5587ea24c63a8c30094c8ff4d990780b5033ee15c40/django-5.1.10.tar.gz", hash = "sha256:73e5d191421d177803dbd5495d94bc7d06d156df9561f4eea9e11b4994c07137", size = 10714538, upload-time = "2025-06-04T13:53:18.805Z" } sdist = { url = "https://files.pythonhosted.org/packages/83/80/bf0f9b0aa434fca2b46fc6a31c39b08ea714b87a0a72a16566f053fb05a8/django-5.1.11.tar.gz", hash = "sha256:3bcdbd40e4d4623b5e04f59c28834323f3086df583058e65ebce99f9982385ce", size = 10734926, upload-time = "2025-06-10T10:12:48.229Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/fc/80dc741ba0acb3241aac1213d7272c573d52d8a62ec2c69e9b3bef1547f2/django-5.1.10-py3-none-any.whl", hash = "sha256:19c9b771e9cf4de91101861aadd2daaa159bcf10698ca909c5755c88e70ccb84", size = 8277457, upload-time = "2025-06-04T13:53:07.676Z" }, { url = "https://files.pythonhosted.org/packages/59/91/2972ce330c6c0bd5b3200d4c2ad5cbf47eecff5243220c5a56444d3267a0/django-5.1.11-py3-none-any.whl", hash = "sha256:e48091f364007068728aca938e7450fbfe3f2217079bfd2b8af45122585acf64", size = 8277453, upload-time = "2025-06-10T10:12:42.236Z" },
] ]
[[package]] [[package]]
@ -1386,7 +1386,7 @@ wheels = [
[[package]] [[package]]
name = "google-api-python-client" name = "google-api-python-client"
version = "2.171.0" version = "2.172.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "google-api-core" }, { name = "google-api-core" },
@ -1395,7 +1395,10 @@ dependencies = [
{ name = "httplib2" }, { name = "httplib2" },
{ name = "uritemplate" }, { name = "uritemplate" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/35/99/237cd2510aecca9fabb54007e58553274cc43cb3c18512ee1ea574d11b87/google_api_python_client-2.171.0.tar.gz", hash = "sha256:057a5c08d28463c6b9eb89746355de5f14b7ed27a65c11fdbf1d06c66bb66b23", size = 13028937, upload-time = "2025-06-03T18:57:38.732Z" } sdist = { url = "https://files.pythonhosted.org/packages/02/69/c0cec6be5878d4de161f64096edb3d4a2d1a838f036b8425ea8358d0dfb3/google_api_python_client-2.172.0.tar.gz", hash = "sha256:dcb3b7e067154b2aa41f1776cf86584a5739c0ac74e6ff46fc665790dca0e6a6", size = 13074841, upload-time = "2025-06-10T16:58:41.181Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/15/fc/8850ccf21c5df43faeaf8bba8c4149ee880b41b8dc7066e3259bcfd921ca/google_api_python_client-2.172.0-py3-none-any.whl", hash = "sha256:9f1b9a268d5dc1228207d246c673d3a09ee211b41a11521d38d9212aeaa43af7", size = 13595800, upload-time = "2025-06-10T16:58:38.143Z" },
]
[[package]] [[package]]
name = "google-auth" name = "google-auth"
@ -1769,7 +1772,7 @@ wheels = [
[[package]] [[package]]
name = "kubernetes" name = "kubernetes"
version = "32.0.1" version = "33.1.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi" }, { name = "certifi" },
@ -1784,9 +1787,9 @@ dependencies = [
{ name = "urllib3" }, { name = "urllib3" },
{ name = "websocket-client" }, { name = "websocket-client" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/b7/e8/0598f0e8b4af37cd9b10d8b87386cf3173cb8045d834ab5f6ec347a758b3/kubernetes-32.0.1.tar.gz", hash = "sha256:42f43d49abd437ada79a79a16bd48a604d3471a117a8347e87db693f2ba0ba28", size = 946691, upload-time = "2025-02-18T21:06:34.148Z" } sdist = { url = "https://files.pythonhosted.org/packages/ae/52/19ebe8004c243fdfa78268a96727c71e08f00ff6fe69a301d0b7fcbce3c2/kubernetes-33.1.0.tar.gz", hash = "sha256:f64d829843a54c251061a8e7a14523b521f2dc5c896cf6d65ccf348648a88993", size = 1036779, upload-time = "2025-06-09T21:57:58.521Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/08/10/9f8af3e6f569685ce3af7faab51c8dd9d93b9c38eba339ca31c746119447/kubernetes-32.0.1-py2.py3-none-any.whl", hash = "sha256:35282ab8493b938b08ab5526c7ce66588232df00ef5e1dbe88a419107dc10998", size = 1988070, upload-time = "2025-02-18T21:06:31.391Z" }, { url = "https://files.pythonhosted.org/packages/89/43/d9bebfc3db7dea6ec80df5cb2aad8d274dd18ec2edd6c4f21f32c237cbbb/kubernetes-33.1.0-py2.py3-none-any.whl", hash = "sha256:544de42b24b64287f7e0aa9513c93cb503f7f40eea39b20f66810011a86eabc5", size = 1941335, upload-time = "2025-06-09T21:57:56.327Z" },
] ]
[[package]] [[package]]
@ -2052,7 +2055,7 @@ wheels = [
[[package]] [[package]]
name = "msgraph-sdk" name = "msgraph-sdk"
version = "1.32.0" version = "1.33.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "azure-identity" }, { name = "azure-identity" },
@ -2062,9 +2065,9 @@ dependencies = [
{ name = "microsoft-kiota-serialization-text" }, { name = "microsoft-kiota-serialization-text" },
{ name = "msgraph-core" }, { name = "msgraph-core" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e0/2a/a5d67f631f8a0a9daa9059eed0f6703002efdf75f1d7575a289e23aceb5e/msgraph_sdk-1.32.0.tar.gz", hash = "sha256:485b25420ec4b3bf9c9d7abea166f9c51da99d9aad1bd57c191f84569af35143", size = 6628831, upload-time = "2025-06-03T18:22:21.526Z" } sdist = { url = "https://files.pythonhosted.org/packages/61/2b/41ae74a4277210a0f7b102f8c34c6304f8a2aeb3ccc94c7c4cb14542ebba/msgraph_sdk-1.33.0.tar.gz", hash = "sha256:3f92d55eee4816e554831d1f6d8db6842ce1717f6f6d92bbb8ba98c4802821df", size = 6628201, upload-time = "2025-06-10T18:43:18.811Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/59/7e0f170611eb1a213940816703522ecca47ddffc149ef8e001e4789ddf13/msgraph_sdk-1.32.0-py3-none-any.whl", hash = "sha256:d1256f1669706c2703963ef0103470ce7a1b8a06f555506fc6b5f1069847877b", size = 27190465, upload-time = "2025-06-03T18:22:18.944Z" }, { url = "https://files.pythonhosted.org/packages/fd/b3/6eca02d93c4b47b7a3b4388b65c4390eba5962379b2a9e27685b052dc428/msgraph_sdk-1.33.0-py3-none-any.whl", hash = "sha256:d42b8c39e2cdc386243d1d80df9481e9bc660db0803307a17fb3783085b19a42", size = 27215918, upload-time = "2025-06-10T18:43:15.155Z" },
] ]
[[package]] [[package]]
@ -2460,7 +2463,7 @@ wheels = [
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.11.5" version = "2.11.7"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "annotated-types" }, { name = "annotated-types" },
@ -2468,9 +2471,9 @@ dependencies = [
{ name = "typing-extensions" }, { name = "typing-extensions" },
{ name = "typing-inspection" }, { name = "typing-inspection" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload-time = "2025-05-22T21:18:08.761Z" } sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload-time = "2025-05-22T21:18:06.329Z" }, { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@ -2928,15 +2931,15 @@ wheels = [
[[package]] [[package]]
name = "sentry-sdk" name = "sentry-sdk"
version = "2.29.1" version = "2.30.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi" }, { name = "certifi" },
{ name = "urllib3" }, { name = "urllib3" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/22/67/d552a5f8e5a6a56b2feea6529e2d8ccd54349084c84176d5a1f7295044bc/sentry_sdk-2.29.1.tar.gz", hash = "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", size = 325518, upload-time = "2025-05-19T14:27:38.512Z" } sdist = { url = "https://files.pythonhosted.org/packages/04/4c/af31e0201b48469786ddeb1bf6fd3dfa3a291cc613a0fe6a60163a7535f9/sentry_sdk-2.30.0.tar.gz", hash = "sha256:436369b02afef7430efb10300a344fb61a11fe6db41c2b11f41ee037d2dd7f45", size = 326767, upload-time = "2025-06-12T10:34:34.733Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/f0/e5/da07b0bd832cefd52d16f2b9bbbe31624d57552602c06631686b93ccb1bd/sentry_sdk-2.29.1-py2.py3-none-any.whl", hash = "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19", size = 341553, upload-time = "2025-05-19T14:27:36.882Z" }, { url = "https://files.pythonhosted.org/packages/5a/99/31ac6faaae33ea698086692638f58d14f121162a8db0039e68e94135e7f1/sentry_sdk-2.30.0-py2.py3-none-any.whl", hash = "sha256:59391db1550662f746ea09b483806a631c3ae38d6340804a1a4c0605044f6877", size = 343149, upload-time = "2025-06-12T10:34:32.896Z" },
] ]
[[package]] [[package]]
@ -3148,7 +3151,7 @@ wheels = [
[[package]] [[package]]
name = "twilio" name = "twilio"
version = "9.6.2" version = "9.6.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "aiohttp" }, { name = "aiohttp" },
@ -3156,9 +3159,9 @@ dependencies = [
{ name = "pyjwt" }, { name = "pyjwt" },
{ name = "requests" }, { name = "requests" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/fa/c9/441a07f6552f2b504812501d56c41bd85b02afeef6c23ab8baf41ed6c70e/twilio-9.6.2.tar.gz", hash = "sha256:5da13bb497e39ece34cb9f2b3bc911f3288928612748f7688b3bda262c2767a1", size = 1041300, upload-time = "2025-05-29T12:25:04.59Z" } sdist = { url = "https://files.pythonhosted.org/packages/fb/af/1b401bc4cfd3eb41c7e2a98d0040d2bcfd2ad3217f3163401121179b3fb3/twilio-9.6.3.tar.gz", hash = "sha256:16a8c2ab9550343c25c8a195f31db9e230d9b341eca31ebdd301109910fd9730", size = 1041494, upload-time = "2025-06-12T10:40:55.63Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/67/91/382e83e5d205a7ae4325b66d40cd2fa6ce85526f2ed8fc553265e19abbe4/twilio-9.6.2-py2.py3-none-any.whl", hash = "sha256:8d4af6f42850734a921857df42940f7fed84e3e4a508d0d6bef5b9fb7dc08357", size = 1909253, upload-time = "2025-05-29T12:25:02.521Z" }, { url = "https://files.pythonhosted.org/packages/c9/35/d61a3581eb223e5e1fc0add1c397d7bb60014b22790e8f89aa5eb4e41e04/twilio-9.6.3-py2.py3-none-any.whl", hash = "sha256:a9b2cf11b0718394f12c43585ca25b9094f12b82ff975f1561fcec7f0f6f49b2", size = 1909549, upload-time = "2025-06-12T10:40:53.67Z" },
] ]
[[package]] [[package]]
@ -3388,7 +3391,7 @@ wheels = [
[[package]] [[package]]
name = "webauthn" name = "webauthn"
version = "2.5.2" version = "2.6.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "asn1crypto" }, { name = "asn1crypto" },
@ -3396,9 +3399,9 @@ dependencies = [
{ name = "cryptography" }, { name = "cryptography" },
{ name = "pyopenssl" }, { name = "pyopenssl" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/8d/92/8d2a4eec83d8e7feacdaad37c6eb6eb922100cecce5c14a41d8069a59a03/webauthn-2.5.2.tar.gz", hash = "sha256:09c13dfc1c68c810f32fa4d89b1d37acb9f9ae9091c9d7019e313be4525a95ef", size = 124114, upload-time = "2025-03-07T19:44:05.243Z" } sdist = { url = "https://files.pythonhosted.org/packages/63/38/5792cb2034673c162a721df0ad65825699516ee0c938a65670ad3cdabf6c/webauthn-2.6.0.tar.gz", hash = "sha256:13cf5b009a64cef569599ffecf24550df1d7c0cd4fbaea870f937148484a80b4", size = 123608, upload-time = "2025-06-16T22:25:26.76Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/fe/f6ae41de9f383439e30b303a67f6f45d2fceabedaedc34c62f74d58c5c73/webauthn-2.5.2-py3-none-any.whl", hash = "sha256:44246e496e617eb5e2f51165046b9f0197fcdf470f69cd6734061a27ba365f8e", size = 71624, upload-time = "2025-03-07T19:44:03.728Z" }, { url = "https://files.pythonhosted.org/packages/56/c5/b1bba7f6a50caca77f37003e098f48f8dc68d990aba8a03ac8376016430b/webauthn-2.6.0-py3-none-any.whl", hash = "sha256:459973eb5780c1f41bec42b682acf587456b185733398a0b99a0714705b79447", size = 71189, upload-time = "2025-06-16T22:25:25.535Z" },
] ]
[[package]] [[package]]

1874
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -86,14 +86,14 @@
"@codemirror/lang-css": "^6.3.1", "@codemirror/lang-css": "^6.3.1",
"@codemirror/lang-html": "^6.4.9", "@codemirror/lang-html": "^6.4.9",
"@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-javascript": "^6.2.4",
"@codemirror/lang-python": "^6.1.6", "@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-xml": "^6.1.0", "@codemirror/lang-xml": "^6.1.0",
"@codemirror/legacy-modes": "^6.4.1", "@codemirror/legacy-modes": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@floating-ui/dom": "^1.6.11", "@floating-ui/dom": "^1.6.11",
"@formatjs/intl-listformat": "^7.7.11", "@formatjs/intl-listformat": "^7.7.11",
"@fortawesome/fontawesome-free": "^6.6.0", "@fortawesome/fontawesome-free": "^6.7.2",
"@goauthentik/api": "^2025.6.0-1749054550", "@goauthentik/api": "^2025.6.2-1750112513",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",
"@lit/localize": "^0.12.2", "@lit/localize": "^0.12.2",
"@lit/reactive-element": "^2.0.4", "@lit/reactive-element": "^2.0.4",
@ -102,10 +102,9 @@
"@open-wc/lit-helpers": "^0.7.0", "@open-wc/lit-helpers": "^0.7.0",
"@patternfly/elements": "^4.1.0", "@patternfly/elements": "^4.1.0",
"@patternfly/patternfly": "^4.224.2", "@patternfly/patternfly": "^4.224.2",
"@sentry/browser": "^9.24.0", "@sentry/browser": "^9.30.0",
"@spotlightjs/spotlight": "^2.13.3", "@spotlightjs/spotlight": "^3.0.0",
"@webcomponents/webcomponentsjs": "^2.8.0", "@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
"change-case": "^5.4.4", "change-case": "^5.4.4",
"chart.js": "^4.4.9", "chart.js": "^4.4.9",
"chartjs-adapter-date-fns": "^3.0.0", "chartjs-adapter-date-fns": "^3.0.0",
@ -121,7 +120,7 @@
"hastscript": "^9.0.1", "hastscript": "^9.0.1",
"lit": "^3.2.0", "lit": "^3.2.0",
"md-front-matter": "^1.0.4", "md-front-matter": "^1.0.4",
"mermaid": "^11.4.1", "mermaid": "^11.6.0",
"rapidoc": "^9.3.8", "rapidoc": "^9.3.8",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
@ -135,8 +134,9 @@
"remark-mdx-frontmatter": "^5.0.0", "remark-mdx-frontmatter": "^5.0.0",
"style-mod": "^4.1.2", "style-mod": "^4.1.2",
"trusted-types": "^2.0.0", "trusted-types": "^2.0.0",
"ts-pattern": "^5.4.0", "ts-pattern": "^5.7.1",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"webauthn-polyfills": "^0.1.7",
"webcomponent-qr-code": "^1.2.0", "webcomponent-qr-code": "^1.2.0",
"yaml": "^2.8.0" "yaml": "^2.8.0"
}, },
@ -164,22 +164,22 @@
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/grecaptcha": "^3.0.9", "@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.3", "@types/guacamole-common-js": "^1.5.3",
"@types/mocha": "^10.0.8", "@types/mocha": "^10.0.10",
"@types/node": "^22.15.21", "@types/node": "^22.15.21",
"@types/react": "^19.1.5", "@types/react": "^19.1.5",
"@types/react-dom": "^19.1.5", "@types/react-dom": "^19.1.5",
"@typescript-eslint/eslint-plugin": "^8.8.0", "@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0", "@typescript-eslint/parser": "^8.8.0",
"@wdio/browser-runner": "9.4", "@wdio/browser-runner": "9.15",
"@wdio/cli": "9.4", "@wdio/cli": "9.15",
"@wdio/spec-reporter": "^9.1.2", "@wdio/spec-reporter": "^9.15.0",
"@web/test-runner": "^0.20.2", "@web/test-runner": "^0.20.2",
"chromedriver": "^136.0.3", "chromedriver": "^136.0.3",
"esbuild": "^0.25.5", "esbuild": "^0.25.5",
"esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-polyfill-node": "^0.3.0", "esbuild-plugin-polyfill-node": "^0.3.0",
"esbuild-plugins-node-modules-polyfill": "^1.7.0", "esbuild-plugins-node-modules-polyfill": "^1.7.0",
"eslint": "^9.28.0", "eslint": "^9.29.0",
"eslint-plugin-lit": "^2.1.1", "eslint-plugin-lit": "^2.1.1",
"eslint-plugin-wc": "^3.0.1", "eslint-plugin-wc": "^3.0.1",
"github-slugger": "^2.0.0", "github-slugger": "^2.0.0",
@ -194,7 +194,7 @@
"storybook-addon-mock": "^5.0.0", "storybook-addon-mock": "^5.0.0",
"turnstile-types": "^1.2.3", "turnstile-types": "^1.2.3",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"typescript-eslint": "^8.33.0", "typescript-eslint": "^8.34.1",
"vite-plugin-lit-css": "^2.0.0", "vite-plugin-lit-css": "^2.0.0",
"vite-tsconfig-paths": "^5.0.1", "vite-tsconfig-paths": "^5.0.1",
"wireit": "^0.14.12" "wireit": "^0.14.12"

View File

@ -14,7 +14,7 @@ declare module "module" {
* const relativeDirname = dirname(fileURLToPath(import.meta.url)); * const relativeDirname = dirname(fileURLToPath(import.meta.url));
* ``` * ```
*/ */
// eslint-disable-next-line no-var
var __dirname: string; var __dirname: string;
} }
} }

View File

@ -11,11 +11,11 @@
}, },
"dependencies": { "dependencies": {
"@goauthentik/api": "^2024.6.0-1719577139", "@goauthentik/api": "^2024.6.0-1719577139",
"base64-js": "^1.5.1",
"bootstrap": "^4.6.1", "bootstrap": "^4.6.1",
"formdata-polyfill": "^4.0.10", "formdata-polyfill": "^4.0.10",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"weakmap-polyfill": "^2.0.4" "weakmap-polyfill": "^2.0.4",
"webauthn-polyfills": "^0.1.7"
}, },
"devDependencies": { "devDependencies": {
"@goauthentik/core": "^1.0.0", "@goauthentik/core": "^1.0.0",

View File

@ -1,7 +1,7 @@
import { fromByteArray } from "base64-js";
import "formdata-polyfill"; import "formdata-polyfill";
import $ from "jquery"; import $ from "jquery";
import "weakmap-polyfill"; import "weakmap-polyfill";
import "webauthn-polyfills";
import { import {
type AuthenticatorValidationChallenge, type AuthenticatorValidationChallenge,
@ -257,47 +257,9 @@ class AutosubmitStage extends Stage<AutosubmitChallenge> {
} }
} }
export interface Assertion {
id: string;
rawId: string;
type: string;
registrationClientExtensions: string;
response: {
clientDataJSON: string;
attestationObject: string;
};
}
export interface AuthAssertion {
id: string;
rawId: string;
type: string;
assertionClientExtensions: string;
response: {
clientDataJSON: string;
authenticatorData: string;
signature: string;
userHandle: string | null;
};
}
class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> { class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
deviceChallenge?: DeviceChallenge; deviceChallenge?: DeviceChallenge;
b64enc(buf: Uint8Array): string {
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
b64RawEnc(buf: Uint8Array): string {
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_");
}
u8arr(input: string): Uint8Array {
return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) =>
c.charCodeAt(0),
);
}
checkWebAuthnSupport(): boolean { checkWebAuthnSupport(): boolean {
if ("credentials" in navigator) { if ("credentials" in navigator) {
return true; return true;
@ -310,98 +272,6 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
return false; return false;
} }
/**
* Transforms items in the credentialCreateOptions generated on the server
* into byte arrays expected by the navigator.credentials.create() call
*/
transformCredentialCreateOptions(
credentialCreateOptions: PublicKeyCredentialCreationOptions,
userId: string,
): PublicKeyCredentialCreationOptions {
const user = credentialCreateOptions.user;
// Because json can't contain raw bytes, the server base64-encodes the User ID
// So to get the base64 encoded byte array, we first need to convert it to a regular
// string, then a byte array, re-encode it and wrap that in an array.
const stringId = decodeURIComponent(window.atob(userId));
user.id = this.u8arr(this.b64enc(this.u8arr(stringId)));
const challenge = this.u8arr(credentialCreateOptions.challenge.toString());
return Object.assign({}, credentialCreateOptions, {
challenge,
user,
});
}
/**
* Transforms the binary data in the credential into base64 strings
* for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion {
const attObj = new Uint8Array(
(newAssertion.response as AuthenticatorAttestationResponse).attestationObject,
);
const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const registrationClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: this.b64enc(rawId),
type: newAssertion.type,
registrationClientExtensions: JSON.stringify(registrationClientExtensions),
response: {
clientDataJSON: this.b64enc(clientDataJSON),
attestationObject: this.b64enc(attObj),
},
};
}
transformCredentialRequestOptions(
credentialRequestOptions: PublicKeyCredentialRequestOptions,
): PublicKeyCredentialRequestOptions {
const challenge = this.u8arr(credentialRequestOptions.challenge.toString());
const allowCredentials = (credentialRequestOptions.allowCredentials || []).map(
(credentialDescriptor) => {
const id = this.u8arr(credentialDescriptor.id.toString());
return Object.assign({}, credentialDescriptor, { id });
},
);
return Object.assign({}, credentialRequestOptions, {
challenge,
allowCredentials,
});
}
/**
* Encodes the binary data in the assertion into strings for posting to the server.
* @param {PublicKeyCredential} newAssertion
*/
transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion {
const response = newAssertion.response as AuthenticatorAssertionResponse;
const authData = new Uint8Array(response.authenticatorData);
const clientDataJSON = new Uint8Array(response.clientDataJSON);
const rawId = new Uint8Array(newAssertion.rawId);
const sig = new Uint8Array(response.signature);
const assertionClientExtensions = newAssertion.getClientExtensionResults();
return {
id: newAssertion.id,
rawId: this.b64enc(rawId),
type: newAssertion.type,
assertionClientExtensions: JSON.stringify(assertionClientExtensions),
response: {
clientDataJSON: this.b64RawEnc(clientDataJSON),
signature: this.b64RawEnc(sig),
authenticatorData: this.b64RawEnc(authData),
userHandle: null,
},
};
}
render() { render() {
if (this.challenge.deviceChallenges.length === 1) { if (this.challenge.deviceChallenges.length === 1) {
this.deviceChallenge = this.challenge.deviceChallenges[0]; this.deviceChallenge = this.challenge.deviceChallenges[0];
@ -505,8 +375,8 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
`); `);
navigator.credentials navigator.credentials
.get({ .get({
publicKey: this.transformCredentialRequestOptions( publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(
this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions, this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptionsJSON,
), ),
}) })
.then((assertion) => { .then((assertion) => {
@ -514,15 +384,9 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
throw new Error("No assertion"); throw new Error("No assertion");
} }
try { try {
// we now have an authentication assertion! encode the byte arrays contained
// in the assertion data as strings for posting to the server
const transformedAssertionForServer = this.transformAssertionForServer(
assertion as PublicKeyCredential,
);
// post the assertion to the server for verification. // post the assertion to the server for verification.
this.executor.submit({ this.executor.submit({
webauthn: transformedAssertionForServer, webauthn: (assertion as PublicKeyCredential).toJSON(),
}); });
} catch (err) { } catch (err) {
throw new Error(`Error when validating assertion on server: ${err}`); throw new Error(`Error when validating assertion on server: ${err}`);

View File

@ -13,7 +13,7 @@ import PFList from "@patternfly/patternfly/components/List/list.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css"; import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
import { EventActions } from "@goauthentik/api"; import { EventActions, EventsEventsVolumeListRequest } from "@goauthentik/api";
@customElement("ak-admin-dashboard-users") @customElement("ak-admin-dashboard-users")
export class DashboardUserPage extends AKElement { export class DashboardUserPage extends AKElement {
@ -46,9 +46,9 @@ export class DashboardUserPage extends AKElement {
<ak-aggregate-card header=${msg("Users created per day in the last month")}> <ak-aggregate-card header=${msg("Users created per day in the last month")}>
<ak-charts-admin-model-per-day <ak-charts-admin-model-per-day
.query=${{ .query=${{
context__model__app: "authentik_core", contextModelApp: "authentik_core",
context__model__model_name: "user", contextModelName: "user",
}} } as EventsEventsVolumeListRequest}
label=${msg("Users created")} label=${msg("Users created")}
> >
</ak-charts-admin-model-per-day> </ak-charts-admin-model-per-day>

View File

@ -88,7 +88,8 @@ export class RecentEventsCard extends Table<Event> {
} }
return super.renderEmpty( return super.renderEmpty(
html`<ak-empty-state header=${msg("No Events found.")}> html`<ak-empty-state
><span slot="header">${msg("No Events found.")}</span>
<div slot="body">${msg("No matching events could be found.")}</div> <div slot="body">${msg("No matching events could be found.")}</div>
</ak-empty-state>`, </ak-empty-state>`,
); );

Some files were not shown because too many files have changed in this diff Show More