Compare commits

..

66 Commits

Author SHA1 Message Date
Jens Langhammer
1db6104bef flows: return correct status code on error
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 13:23:24 +01:00
Jens L.
62dc04a684 stages: remove more global state (#18641)
* add auth for active flow

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

* migrate duo

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

* migrate sms

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

* migrate consent

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

* migrate email and fix broken tests

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

* fallback

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

* merge flow plan when restoring from token

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 13:12:35 +01:00
dependabot[bot]
68f3bf6ec1 web: bump the storybook group across 1 directory with 5 updates (#18817)
Bumps the storybook group with 4 updates in the /web directory: [@storybook/addon-docs](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/docs), [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links), [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) and [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite).


Updates `@storybook/addon-docs` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/addons/docs)

Updates `@storybook/addon-links` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/addons/links)

Updates `@storybook/web-components` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/frameworks/web-components-vite)

Updates `storybook` from 10.1.7 to 10.1.8
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v10.1.8/code/core)

---
updated-dependencies:
- dependency-name: "@storybook/addon-docs"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-version: 10.1.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 13:02:01 +01:00
dependabot[bot]
8234613b76 web: bump the swc group across 1 directory with 11 updates (#18923)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-darwin-arm64` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-darwin-x64` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-linux-arm64-gnu` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-linux-arm64-musl` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-linux-x64-gnu` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-linux-x64-musl` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-win32-arm64-msvc` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-win32-ia32-msvc` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

Updates `@swc/core-win32-x64-msvc` from 1.15.5 to 1.15.6
- [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.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-18 13:01:49 +01:00
dependabot[bot]
eec998cc8d lifecycle/aws: bump aws-cdk from 2.1100.0 to 2.1100.1 in /lifecycle/aws (#18922)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1100.0 to 2.1100.1.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1100.1/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1100.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-12-18 13:00:30 +01:00
authentik-automation[bot]
d01aa6bebf core, web: update translations (#18920)
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-12-18 12:57:02 +01:00
dependabot[bot]
cbbf315662 web: bump knip from 5.74.0 to 5.75.1 in /web (#18924)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.74.0 to 5.75.1.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/5.75.1/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.75.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-12-18 12:55:58 +01:00
dependabot[bot]
45ca767fd8 web: bump chromedriver from 143.0.1 to 143.0.2 in /web (#18926)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 143.0.1 to 143.0.2.
- [Commits](https://github.com/giggio/node-chromedriver/compare/143.0.1...143.0.2)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-version: 143.0.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-12-18 12:55:32 +01:00
dependabot[bot]
5d3e2e89e0 core: bump goauthentik/fips-debian from 189345a to 10dadf1 (#18927)
Bumps goauthentik/fips-debian from `189345a` to `10dadf1`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  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-12-18 12:55:06 +01:00
Marcelo Elizeche Landó
5e2f261a0c tests/e2e: handle StaleElementReferenceException in parse_json_content (#18842)
Add retry logic and safer text recover
2025-12-17 20:27:03 -03:00
Tana M Berry
10a421e678 web/admin: add UI copy to RBAC modal (#18917)
add UI copy to RBAC modal
2025-12-17 20:23:20 +00:00
Marc 'risson' Schmitt
668ad3dadf root: fix docker-compose data mount (#18903) 2025-12-17 16:33:50 -03:00
João C. Fernandes
e7903d5391 core/groups: optimize prefetch queries to fetch only required fields (#18448)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 17:15:31 +00:00
Jens L.
e38fffc44c web/admin: reword some things on the device view page (#18785)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-17 17:33:36 +01:00
Marc 'risson' Schmitt
4bc2bca448 website/docs: 2025.12: remove superfluous changes (#18910) 2025-12-17 16:24:12 +00:00
Jens L.
48916303d8 api: fix latest version for public schema (#18902) 2025-12-17 15:12:14 +00:00
Marc 'risson' Schmitt
d28109da6a ci/release-tag: checkout correct branch for make test-docker (#18880) 2025-12-17 15:08:05 +00:00
Marc 'risson' Schmitt
3bd299d52a api: fix page_size with invalid query param (#18879) 2025-12-17 15:06:22 +00:00
Tana M Berry
57418582c5 website/docs: added list of Int Guide contributors (also edited frontmatter) (#18888)
* alphabetize

* added frontmatter

* Apply suggestions from code review

Signed-off-by: Dominic R <dominic@sdko.org>

---------

Signed-off-by: Dominic R <dominic@sdko.org>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-17 09:01:05 -06:00
Marc 'risson' Schmitt
f37958bcd0 tasks/middleware: close connections on worker status update database error (#18881) 2025-12-17 15:45:37 +01:00
dependabot[bot]
8931b621b4 core: bump goauthentik.io/api/v3 from 3.2026020.1 to 3.2026020.3 (#18892)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 13:24:57 +00:00
dependabot[bot]
9d3d96bab1 web: bump the eslint group across 1 directory with 5 updates (#18851)
Bumps the eslint group with 5 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.1` | `9.39.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.49.0` | `8.50.0` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.49.0` | `8.50.0` |
| [eslint](https://github.com/eslint/eslint) | `9.39.1` | `9.39.2` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.49.0` | `8.50.0` |



Updates `@eslint/js` from 9.39.1 to 9.39.2
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.2/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.49.0 to 8.50.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.50.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.49.0 to 8.50.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.50.0/packages/parser)

Updates `eslint` from 9.39.1 to 9.39.2
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.1...v9.39.2)

Updates `typescript-eslint` from 8.49.0 to 8.50.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.50.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: eslint
- dependency-name: eslint
  dependency-version: 9.39.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: eslint
- dependency-name: typescript-eslint
  dependency-version: 8.50.0
  dependency-type: direct:production
  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-12-17 13:15:56 +01:00
dependabot[bot]
712f0ed95e web: bump the swc group across 1 directory with 11 updates (#18818)
Bumps the swc group with 1 update in the /web directory: [@swc/core](https://github.com/swc-project/swc).


Updates `@swc/core` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-darwin-arm64` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-darwin-x64` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-linux-arm-gnueabihf` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-linux-arm64-gnu` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-linux-arm64-musl` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-linux-x64-gnu` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-linux-x64-musl` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-win32-arm64-msvc` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-win32-ia32-msvc` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

Updates `@swc/core-win32-x64-msvc` from 1.15.3 to 1.15.4
- [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.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-arm64"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-darwin-x64"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm-gnueabihf"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-gnu"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-arm64-musl"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-gnu"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-linux-x64-musl"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-arm64-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-ia32-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
- dependency-name: "@swc/core-win32-x64-msvc"
  dependency-version: 1.15.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: swc
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:32:37 +01:00
dependabot[bot]
1cd9c7bf9d web: bump the goauthentik group across 1 directory with 3 updates (#18819)
Bumps the goauthentik group with 3 updates in the /web directory: [@goauthentik/esbuild-plugin-live-reload](https://github.com/goauthentik/authentik/tree/HEAD/packages/esbuild-plugin-live-reload), [@goauthentik/eslint-config](https://github.com/goauthentik/authentik/tree/HEAD/packages/eslint-config) and [@goauthentik/prettier-config](https://github.com/goauthentik/authentik/tree/HEAD/packages/prettier-config).


Updates `@goauthentik/esbuild-plugin-live-reload` from 1.3.1 to 1.4.0
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/esbuild-plugin-live-reload)

Updates `@goauthentik/eslint-config` from 1.1.1 to 1.2.0
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/eslint-config)

Updates `@goauthentik/prettier-config` from 3.2.1 to 3.3.1
- [Release notes](https://github.com/goauthentik/authentik/releases)
- [Commits](https://github.com/goauthentik/authentik/commits/HEAD/packages/prettier-config)

---
updated-dependencies:
- dependency-name: "@goauthentik/esbuild-plugin-live-reload"
  dependency-version: 1.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
- dependency-name: "@goauthentik/eslint-config"
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
- dependency-name: "@goauthentik/prettier-config"
  dependency-version: 3.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: goauthentik
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:32:22 +01:00
dependabot[bot]
fb23751079 lifecycle/aws: bump aws-cdk from 2.1034.0 to 2.1100.0 in /lifecycle/aws (#18850)
Bumps [aws-cdk](https://github.com/aws/aws-cdk-cli/tree/HEAD/packages/aws-cdk) from 2.1034.0 to 2.1100.0.
- [Release notes](https://github.com/aws/aws-cdk-cli/releases)
- [Commits](https://github.com/aws/aws-cdk-cli/commits/aws-cdk@v2.1100.0/packages/aws-cdk)

---
updated-dependencies:
- dependency-name: aws-cdk
  dependency-version: 2.1100.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-12-17 12:32:01 +01:00
dependabot[bot]
e49aace000 web: bump @sentry/browser from 10.30.0 to 10.31.0 in /web in the sentry group across 1 directory (#18893)
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 10.30.0 to 10.31.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/10.30.0...10.31.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-version: 10.31.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-12-17 12:31:27 +01:00
dependabot[bot]
876b299f30 web: bump the bundler group across 1 directory with 7 updates (#18894)
Bumps the bundler group with 2 updates in the /web directory: [@vitest/browser](https://github.com/vitest-dev/vitest/tree/HEAD/packages/browser) and [esbuild](https://github.com/evanw/esbuild).


Updates `@vitest/browser` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/browser)

Updates `@vitest/browser-playwright` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/browser-playwright)

Updates `esbuild` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `vitest` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.0.16/packages/vitest)

Updates `@esbuild/darwin-arm64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `@esbuild/linux-arm64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

Updates `@esbuild/linux-x64` from 0.27.1 to 0.27.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.1...v0.27.2)

---
updated-dependencies:
- dependency-name: "@vitest/browser"
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@vitest/browser-playwright"
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: esbuild
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: vitest
  dependency-version: 4.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/darwin-arm64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/linux-arm64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
- dependency-name: "@esbuild/linux-x64"
  dependency-version: 0.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: bundler
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:31:15 +01:00
dependabot[bot]
458439c396 web: bump the rollup group across 1 directory with 4 updates (#18852)
Bumps the rollup group with 4 updates in the /web directory: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup), [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup) and [rollup](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `@rollup/rollup-linux-x64-gnu` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

Updates `rollup` from 4.53.3 to 4.53.4
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v4.53.3...v4.53.4)

---
updated-dependencies:
- dependency-name: "@rollup/rollup-darwin-arm64"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-arm64-gnu"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: "@rollup/rollup-linux-x64-gnu"
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
- dependency-name: rollup
  dependency-version: 4.53.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: rollup
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-17 12:29:44 +01:00
dependabot[bot]
d3d0effe9d web: bump @types/node from 25.0.0 to 25.0.3 in /web (#18895)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.0.0 to 25.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: 25.0.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-12-17 12:29:30 +01:00
dependabot[bot]
413b073191 web: bump knip from 5.73.3 to 5.74.0 in /web (#18896)
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 5.73.3 to 5.74.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/5.74.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 5.74.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-12-17 12:28:48 +01:00
dependabot[bot]
46747ae3f2 core: bump goauthentik/fips-debian from 2f19fc1 to 189345a (#18897)
Bumps goauthentik/fips-debian from `2f19fc1` to `189345a`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  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-12-17 12:28:39 +01:00
dependabot[bot]
d64a3aab39 core: bump astral-sh/uv from 0.9.17 to 0.9.18 (#18898)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.9.17 to 0.9.18.
- [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.9.17...0.9.18)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.9.18
  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-12-17 12:28:28 +01:00
Dominic R
970cddae47 website/integrations: bookstack: fix redir url (#18891) 2025-12-17 08:42:50 +00:00
authentik-automation[bot]
24c4495ac2 core, web: update translations (#18807)
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-17 00:42:03 +00:00
Dewi Roberts
ff38607fa3 web/admin: endpoint: change wording and add helper text (#18871)
* Change wording and add helper text

* Fix helper text

* Setup text change

* Update web/src/admin/endpoints/connectors/agent/AgentConnectorSetup.ts

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

* Update web/src/admin/endpoints/connectors/agent/AgentConnectorSetup.ts

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Update web/src/admin/endpoints/connectors/agent/EnrollmentTokenForm.ts

Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>

* Format.

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@sister.software>
2025-12-16 23:28:29 +00:00
Dominic R
eef8e57f6c web: fix file upload form (#18808)
* web: fix file upload form name mismatch and modal submit promise handling

Fixes the following error:

FileUploadForm.ts:74  POST http://authentik.localhost:9000/api/v3/admin/file/ 405 (Method Not Allowed)
(anonymous) @ fetch.ts:81
fetchApi @ runtime.ts:206
await in fetchApi
request @ runtime.ts:136
await in request
adminFileCreateRaw @ AdminApi.ts:191
adminFileCreate @ AdminApi.ts:206
send @ FileUploadForm.ts:74
submit @ Form.ts:363
(anonymous) @ ModalForm.ts:54
handleEvent @ lit-html.ts:2109
n @ helpers.ts:117Understand this error
Form.ts:403 authentik/forms: API rejected the form submission due to an invalid field that doesn't appear to be in the form. This is likely a bug in authentik. {detail: 'Response returned an error code'}
(anonymous) @ console.ts:39
(anonymous) @ Form.ts:403
Promise.catch
submit @ Form.ts:376
(anonymous) @ ModalForm.ts:54
handleEvent @ lit-html.ts:2109
n @ helpers.ts:117Understand this error
runtime.ts:140 Uncaught (in promise) ResponseError: Response returned an error code
    at mR.request (runtime.ts:140:15)
    at async mR.adminFileCreateRaw (AdminApi.ts:191:26)
    at async mR.adminFileCreate (AdminApi.ts:206:9)

- align file upload rename field with api name so validation errors map correctly
-improve custom filename extension logic to avoid double or incorrect  extensions
- prevent unhandled promise rejections from modal submit click handler and show  missing-form errors to users

* rev

* wip

* Update ModalForm.ts

Signed-off-by: Dominic R <dominic@sdko.org>

* scope better

* fix what it validates against

---------

Signed-off-by: Dominic R <dominic@sdko.org>
2025-12-16 18:37:22 +00:00
Jens L.
603820854b stages/authenticator_*: fix code input field not string (#18875)
* stages/authenticator_*: fix code input field not string

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

* Update authentik/stages/authenticator_totp/stage.py

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-16 19:10:48 +01:00
dependabot[bot]
4ad7f8be2a web: bump vite from 7.2.7 to 7.3.0 in /web (#18854)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.2.7 to 7.3.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.3.0/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.3.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.3.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-12-16 18:44:54 +01:00
Alexander Tereshkin
a605cd1e87 web: fix notification counter (#18781) 2025-12-16 18:41:11 +01:00
Alexander Tereshkin
936789f534 web: add custom message with links for empty data export list (#18830) 2025-12-16 17:36:12 +01:00
Jens L.
2f52d832ab website/docs: 2025.10.3 release notes (#18868)
* website/docs: 2025.10.3 release notes

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

* format

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 16:56:57 +01:00
Marcelo Elizeche Landó
036514730e website/docs: Add docs for passkey autofill (WebauthN Conditional UI) (#18805)
* Add docs for passkey autofill feature

* Apply suggestions from code review

Co-authored-by: Dominic R <dominic@sdko.org>
Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>

* improve configuration section

* remove blank lines

---------

Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-16 15:16:12 +00:00
Simonyi Gergő
d48129ba7b website/docs: adjust RBAC-related details in 2025.12 release notes (#18863)
* website/docs: adjust RBAC-related details in 2025.12 release notes

* adjust wording

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>

---------

Signed-off-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-12-16 15:02:00 +00:00
Jens L.
d219f72ed6 outposts: fix permission errors for related certificates (#18861)
* outposts: fix permission errors for related certificates

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

* fix tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 15:01:25 +01:00
Simonyi Gergő
7b19045431 web/admin/rbac: misc object permission fixes (#18859)
* rbac: relabel "Assign role permissions" button

* fix object permissions in permissions table

This should have a backend-based fix in the future.
2025-12-16 14:33:25 +01:00
dependabot[bot]
0027813e4b core: bump library/golang from 5d35fb8 to 8e8f9c8 (#18855)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 13:09:10 +00:00
Simonyi Gergő
a6ebf1074f rbac: alter migrated direct permission roles (#18860)
These should not be marked as managed, because many of these permissions
were created by admins.
2025-12-16 13:40:49 +01:00
Marcelo Elizeche Landó
ea9689c493 core: add skip s3_test_server_available to TestResolveFileUrlS3Backend (#18858)
add skip s3_test_server_available to TestResolveFileUrlS3Backend
2025-12-16 12:59:13 +01:00
Jens L.
06e7335618 ci: replace codecov test-results action (#18862)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-12-16 12:50:54 +01:00
dependabot[bot]
42c4fee053 core: bump goauthentik/fips-debian from c10cd2c to 2f19fc1 (#18856)
Bumps goauthentik/fips-debian from `c10cd2c` to `2f19fc1`.

---
updated-dependencies:
- dependency-name: goauthentik/fips-debian
  dependency-version: trixie-slim-fips
  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-12-16 12:33:13 +01:00
Dominic R
26cfbe67f3 admin/files: fix get_objects_for_user queryset argument in FileUsedByView (#18845)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-16 00:39:13 +00:00
Marcelo Elizeche Landó
2a17024afc core: skip s3 tests if endpoint isn't available (#18841)
skip s3 tests if endpoint isn't available
2025-12-15 20:22:59 -03:00
Connor Peshek
c557b55e0e crypto: Store details parsed from includeDetails in database instead (#18013)
* crypto: Store details parsed from includeDetails in database instead

* fix signal for tests

* Update authentik/crypto/signals.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Update authentik/crypto/apps.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Update authentik/crypto/signals.py

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Connor Peshek <connor@connorpeshek.me>

* Add feedback

* cleanup

* update

* cleanup

* simplify serializer

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

* Update KID for when updating certificates

* lint

---------

Signed-off-by: Connor Peshek <connor@connorpeshek.me>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: connor peshek <connorpeshek@connors-MacBook-Pro.local>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-12-15 13:50:16 -06:00
Roi Gabay
f56e354e38 website/docs: add jellyseer integration doc (#18812)
* website/docs: add jellyseer integration doc

* Slight tweaks

* Apply suggestions from code review

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

* Update website/integrations/media/jellyseerr/index.md

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>

* Apply suggestions from code review

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

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: dewi-tik <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-15 17:12:06 +00:00
Marc 'risson' Schmitt
c50c2b0e0c admin/files: revert add check for /media existence (#18636) (#18829) 2025-12-15 15:29:21 +00:00
dependabot[bot]
662124cac9 core: bump goauthentik.io/api/v3 from 3.2025120.26 to 3.2026020.1 (#18815)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 15:20:48 +00:00
Marc 'risson' Schmitt
3d671a901b packages/django-dramatiq-postgres: broker: close django connections on consumer close (#18833)
Co-authored-by: Norman Ziebal <norman.ziebal@mail.schwarz>
2025-12-15 14:59:51 +01:00
Simonyi Gergő
a7fb031b64 core: remove superuser check from Token list (#18684) 2025-12-15 14:29:42 +01:00
Dewi Roberts
2818b0bbdf website/docs: add icon info to style guide (#18832) 2025-12-15 13:27:22 +00:00
Ryan Pesek
60075e39fb core: list applications fix (#18798) 2025-12-15 13:16:07 +01:00
dependabot[bot]
c112f702b3 ci: bump actions/cache from 5.0.0 to 5.0.1 (#18823)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:15:05 +01:00
dependabot[bot]
42b3323b3d ci: bump actions/download-artifact from 6.0.0 to 7.0.0 (#18825)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:14:22 +01:00
dependabot[bot]
78380831de core: bump goauthentik/fips-debian from 07f41ce to c10cd2c (#18822)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 13:14:09 +01:00
dependabot[bot]
8b5195aeff ci: bump actions/upload-artifact from 5.0.0 to 6.0.0 (#18824)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](330a01c490...b7c566a772)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.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-12-15 12:45:16 +01:00
dependabot[bot]
d762e38027 ci: bump astral-sh/setup-uv from 7.1.5 to 7.1.6 in /.github/actions/setup (#18826)
ci: bump astral-sh/setup-uv in /.github/actions/setup

Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 7.1.5 to 7.1.6.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](ed21f2f24f...681c641aba)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 7.1.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-12-15 12:36:47 +01:00
Marcelo Elizeche Landó
e427cb611e root: Add macOS support for sed in Makefile (#18795)
Add macOS support for sed
2025-12-15 12:09:35 +01:00
112 changed files with 2250 additions and 2622 deletions

View File

@@ -1,63 +0,0 @@
# syntax=docker/dockerfile:1
# Start from the same FIPS Python base as production (python-base stage)
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips@sha256:700fc8c1e290bd14e5eaca50b1d8e8c748c820010559cbfb4c4f8dfbe2c4c9ff
USER root
# Setup environment matching production python-base stage
ENV VENV_PATH="/ak-root/.venv" \
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_NATIVE_TLS=1 \
UV_PYTHON_DOWNLOADS=0
WORKDIR /ak-root
# Copy uv package manager
COPY --from=ghcr.io/astral-sh/uv:0.9.7@sha256:ba4857bf2a068e9bc0e64eed8563b065908a4cd6bfb66b531a9c424c8e25e142 /uv /uvx /bin/
# Install build dependencies
RUN rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache && \
apt-get update && \
apt-get install -y --no-install-recommends \
# Build essentials
build-essential pkg-config libffi-dev git binutils \
# cryptography
curl \
# libxml
libxslt-dev zlib1g-dev \
# postgresql
libpq-dev \
# python-kadmin-rs and kerberos testing
clang libkrb5-dev sccache krb5-kdc krb5-admin-server \
# xmlsec
libltdl-dev \
# runit (for chpst command used by lifecycle/ak)
runit \
# sudo (required by devcontainer features)
sudo && \
rm -rf /var/lib/apt/lists/*
# Environment for building native Python packages
ENV UV_NO_BINARY_PACKAGE="cryptography lxml python-kadmin-rs xmlsec" \
RUSTUP_PERMIT_COPY_RENAME="true"
# Create authentik user with proper home directory (required for devcontainer features)
RUN adduser --disabled-password --gecos "" --uid 1000 --home /home/authentik authentik && \
mkdir -p /certs /media /ak-root && \
chown -R authentik:authentik /certs /media /ak-root /home/authentik && \
echo "authentik ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/authentik
# FIPS configuration for Go development
# Don't set GOFIPS/GOFIPS140 globally to avoid breaking Go tools like docker-compose
# These will be set when building/running authentik Go code (see lifecycle/ak and Makefile)
ENV CGO_ENABLED=1
# Set TMPDIR for PID files and temp data
# Use /tmp instead of /dev/shm for development because go run needs to execute binaries
ENV TMPDIR=/tmp
USER authentik

View File

@@ -1,68 +0,0 @@
{
"name": "authentik",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/ak-root",
"containerUser": "authentik",
"remoteUser": "authentik",
"shutdownAction": "stopCompose",
"containerEnv": {
"LOCAL_PROJECT_DIR": "/ak-root"
},
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.24"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "24"
},
"ghcr.io/devcontainers/features/rust:1": {
"version": "latest"
},
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"moby": false
}
},
"mounts": [],
"forwardPorts": [9000, 9443],
"portsAttributes": {
"8000": {
"onAutoForward": "ignore"
},
"3963": {
"onAutoForward": "ignore"
},
"35151": {
"onAutoForward": "ignore"
},
"9901": {
"onAutoForward": "ignore"
}
},
"postCreateCommand": "bash .devcontainer/setup.sh",
"customizations": {
"vscode": {
"extensions": [
"EditorConfig.EditorConfig",
"bashmish.es6-string-css",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"golang.go",
"Gruntfuggly.todo-tree",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.pylint",
"ms-python.python",
"ms-python.vscode-pylance",
"redhat.vscode-yaml",
"Tobermory.es6-string-html",
"charliermarsh.ruff"
],
"settings": {
"python.defaultInterpreterPath": "/ak-root/.venv/bin/python",
"python.terminal.activateEnvironment": true
}
}
}
}

View File

@@ -1,50 +0,0 @@
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
user: authentik
privileged: true
volumes:
- ../:/ak-root
entrypoint: []
command: sleep infinity
depends_on:
postgresql:
condition: service_healthy
env_file: .env
environment:
PATH: "/ak-root/.venv/bin:${PATH}"
ports:
- "9000:9000"
- "9443:9443"
postgresql:
image: docker.io/library/postgres:16
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -d authentik -U postgres"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
volumes:
- postgres-data:/var/lib/postgresql/data
env_file: .env
command: ["postgres", "-c", "log_statement=all", "-c", "log_destination=stderr"]
s3:
image: docker.io/zenko/cloudserver
env_file: .env
environment:
REMOTE_MANAGEMENT_DISABLE: "1"
ports:
- "8020:8000"
volumes:
- s3-data:/usr/src/app/localData
- s3-metadata:/usr/src/app/localMetadata
volumes:
postgres-data:
s3-data:
s3-metadata:

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env bash
set -e
echo "======================================"
echo "Running authentik devcontainer setup"
echo "======================================"
echo ""
echo "Step 1/5: Installing dependencies"
make install
echo ""
echo "Step 2/5: Generating development config"
make gen-dev-config
echo ""
echo "Step 3/5: Running database migrations"
make migrate
echo ""
echo "Step 4/5: Generating API clients"
make gen
echo ""
echo "Step 5/5: Building web assets"
make web
echo ""
echo "======================================"
echo "Setup complete!"
echo "======================================"
echo ""
echo "You can now run:"
echo " - 'make run-server' to start the backend server"
echo " - 'make run-worker' to start the worker (must be ran once after initial setup)"
echo " - 'make web-watch' for live web development"
echo ""

View File

@@ -21,7 +21,7 @@ runs:
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server
- name: Install uv
if: ${{ contains(inputs.dependencies, 'python') }}
uses: astral-sh/setup-uv@ed21f2f24f8dd64503750218de024bcf64c7250a # v5
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v5
with:
enable-cache: true
- name: Setup python

View File

@@ -12,11 +12,11 @@ runs:
with:
flags: ${{ inputs.flags }}
use_oidc: true
- uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1
- uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
with:
flags: ${{ inputs.flags }}
file: unittest.xml
use_oidc: true
report_type: test_results
- name: PostgreSQL Logs
shell: bash
run: |

View File

@@ -41,7 +41,7 @@ jobs:
- working-directory: website/
name: Install Dependencies
run: npm ci
- uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v4
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: |
${{ github.workspace }}/website/api/.docusaurus
@@ -55,7 +55,7 @@ jobs:
env:
NODE_ENV: production
run: npm run build -w api
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
with:
name: api-docs
path: website/api/build
@@ -67,7 +67,7 @@ jobs:
- build
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v5
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v5
with:
name: api-docs
path: website/api/build

View File

@@ -201,7 +201,7 @@ jobs:
run: |
docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull
- id: cache-web
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v4
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v4
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b

View File

@@ -49,8 +49,12 @@ jobs:
test:
name: Pre-release test
runs-on: ubuntu-latest
needs:
- check-inputs
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
- run: make test-docker
bump-authentik:
name: Bump authentik version

View File

@@ -26,7 +26,7 @@ RUN npm run build && \
npm run build:sfe
# Stage 2: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:5d35fb8d28b9095d123b7d96095bbf3750ff18be0a87e5a21c9cffc4351fbf96 AS go-builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:8e8f9c84609b6005af0a4a8227cee53d6226aab1c6dcb22daf5aeeb8b05480e1 AS go-builder
ARG TARGETOS
ARG TARGETARCH
@@ -76,7 +76,7 @@ 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"
# Stage 4: Download uv
FROM ghcr.io/astral-sh/uv:0.9.17@sha256:5cb6b54d2bc3fe2eb9a8483db958a0b9eebf9edff68adedb369df8e7b98711a2 AS uv
FROM ghcr.io/astral-sh/uv:0.9.18@sha256:5713fa8217f92b80223bc83aac7db36ec80a84437dbc0d04bbc659cae030d8c9 AS uv
# Stage 5: Base python image
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips@sha256:700fc8c1e290bd14e5eaca50b1d8e8c748c820010559cbfb4c4f8dfbe2c4c9ff AS python-base

View File

@@ -9,6 +9,13 @@ NPM_VERSION = $(shell python -m scripts.generate_semver)
PY_SOURCES = authentik packages tests scripts lifecycle .github
DOCKER_IMAGE ?= "authentik:test"
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SED_INPLACE = sed -i ''
else
SED_INPLACE = sed -i
endif
GEN_API_TS = gen-ts-api
GEN_API_PY = gen-py-api
GEN_API_GO = gen-go-api
@@ -46,7 +53,7 @@ help: ## Show this help
@echo ""
go-test:
GOFIPS140=latest CGO_ENABLED=1 go test -timeout 0 -v -race -cover ./...
go test -timeout 0 -v -race -cover ./...
test: ## Run the server tests and produce a coverage report (locally)
$(KRB_PATH) uv run coverage run manage.py test --keepdb $(or $(filter-out $@,$(MAKECMDGOALS)),authentik)
@@ -119,8 +126,8 @@ bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx
ifndef version
$(error Usage: make bump version=20xx.xx.xx )
endif
sed -i 's/^version = ".*"/version = "$(version)"/' pyproject.toml
sed -i 's/^VERSION = ".*"/VERSION = "$(version)"/' authentik/__init__.py
$(SED_INPLACE) 's/^version = ".*"/version = "$(version)"/' pyproject.toml
$(SED_INPLACE) 's/^VERSION = ".*"/VERSION = "$(version)"/' authentik/__init__.py
$(MAKE) gen-build gen-compose aws-cfn
npm version --no-git-tag-version --allow-same-version $(version)
cd ${PWD}/web && npm version --no-git-tag-version --allow-same-version $(version)
@@ -155,8 +162,8 @@ gen-diff: ## (Release) generate the changelog diff between the current schema a
/local/schema-old.yml \
/local/schema.yml
rm schema-old.yml
sed -i 's/{/&#123;/g' diff.md
sed -i 's/}/&#125;/g' diff.md
$(SED_INPLACE) 's/{/&#123;/g' diff.md
$(SED_INPLACE) 's/}/&#125;/g' diff.md
npx prettier --write diff.md
gen-clean-ts: ## Remove generated API client for TypeScript

View File

@@ -37,7 +37,7 @@ class VersionSerializer(PassiveSerializer):
def get_version_latest(self, _) -> str:
"""Get latest version from cache"""
if get_current_tenant().schema_name == get_public_schema_name():
if get_current_tenant().schema_name != get_public_schema_name():
return authentik_version()
version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache: # pragma: no cover

View File

@@ -240,7 +240,9 @@ class FileUsedByView(APIView):
for field in fields:
q |= Q(**{field: params.get("name")})
objs = get_objects_for_user(request.user, f"{app}.view_{model_name}", model)
objs = get_objects_for_user(
request.user, f"{app}.view_{model_name}", model.objects.all()
)
objs = objs.filter(q)
for obj in objs:
serializer = UsedBySerializer(

View File

@@ -1,9 +1,4 @@
from pathlib import Path
from django.conf import settings
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.config import CONFIG
class AuthentikFilesConfig(ManagedAppConfig):
@@ -11,20 +6,3 @@ class AuthentikFilesConfig(ManagedAppConfig):
label = "authentik_admin_files"
verbose_name = "authentik Files"
default = True
@ManagedAppConfig.reconcile_global
def check_for_media_mount(self):
if settings.TEST:
return
from authentik.events.models import Event, EventAction
if (
CONFIG.get("storage.media.backend", CONFIG.get("storage.backend", "file")) == "file"
and Path("/media").exists()
):
Event.new(
EventAction.CONFIGURATION_ERROR,
message="/media has been moved to /data/media. "
"Check the release notes for migration steps.",
).save()

View File

@@ -1,10 +1,13 @@
from unittest import skipUnless
from django.test import TestCase
from authentik.admin.files.tests.utils import FileTestS3BackendMixin
from authentik.admin.files.tests.utils import FileTestS3BackendMixin, s3_test_server_available
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG
@skipUnless(s3_test_server_available(), "S3 test server not available")
class TestS3Backend(FileTestS3BackendMixin, TestCase):
"""Test S3 backend functionality"""

View File

@@ -1,10 +1,16 @@
"""Test file service layer"""
from unittest import skipUnless
from django.http import HttpRequest
from django.test import TestCase
from authentik.admin.files.manager import FileManager
from authentik.admin.files.tests.utils import FileTestFileBackendMixin, FileTestS3BackendMixin
from authentik.admin.files.tests.utils import (
FileTestFileBackendMixin,
FileTestS3BackendMixin,
s3_test_server_available,
)
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG
@@ -81,6 +87,7 @@ class TestResolveFileUrlFileBackend(FileTestFileBackendMixin, TestCase):
self.assertEqual(result, "http://example.com/files/media/public/test.png")
@skipUnless(s3_test_server_available(), "S3 test server not available")
class TestResolveFileUrlS3Backend(FileTestS3BackendMixin, TestCase):
@CONFIG.patch("storage.media.s3.custom_domain", "s3.test:8080/test")
@CONFIG.patch("storage.media.s3.secure_urls", False)

View File

@@ -1,11 +1,26 @@
import shutil
import socket
from tempfile import mkdtemp
from urllib.parse import urlparse
from authentik.admin.files.backends.s3 import S3Backend
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG, UNSET
from authentik.lib.generators import generate_id
S3_TEST_ENDPOINT = "http://localhost:8020"
def s3_test_server_available() -> bool:
"""Check if the S3 test server is reachable."""
parsed = urlparse(S3_TEST_ENDPOINT)
try:
with socket.create_connection((parsed.hostname, parsed.port), timeout=2):
return True
except OSError:
return False
class FileTestFileBackendMixin:
def setUp(self):
@@ -57,7 +72,7 @@ class FileTestS3BackendMixin:
for key in s3_config_keys:
self.original_media_s3_settings[key] = CONFIG.get(f"storage.media.s3.{key}", UNSET)
self.media_s3_bucket_name = f"authentik-test-{generate_id(10)}".lower()
CONFIG.set("storage.media.s3.endpoint", "http://localhost:8020")
CONFIG.set("storage.media.s3.endpoint", S3_TEST_ENDPOINT)
CONFIG.set("storage.media.s3.access_key", "accessKey1")
CONFIG.set("storage.media.s3.secret_key", "secretKey1")
CONFIG.set("storage.media.s3.bucket_name", self.media_s3_bucket_name)
@@ -70,7 +85,7 @@ class FileTestS3BackendMixin:
for key in s3_config_keys:
self.original_reports_s3_settings[key] = CONFIG.get(f"storage.reports.s3.{key}", UNSET)
self.reports_s3_bucket_name = f"authentik-test-{generate_id(10)}".lower()
CONFIG.set("storage.reports.s3.endpoint", "http://localhost:8020")
CONFIG.set("storage.reports.s3.endpoint", S3_TEST_ENDPOINT)
CONFIG.set("storage.reports.s3.access_key", "accessKey1")
CONFIG.set("storage.reports.s3.secret_key", "secretKey1")
CONFIG.set("storage.reports.s3.bucket_name", self.reports_s3_bucket_name)

View File

@@ -15,7 +15,9 @@ class Pagination(pagination.PageNumberPagination):
def get_page_size(self, request):
if self.page_size_query_param in request.query_params:
return min(super().get_page_size(request), request.tenant.pagination_max_page_size)
page_size = super().get_page_size(request)
if page_size is not None:
return min(super().get_page_size(request), request.tenant.pagination_max_page_size)
return request.tenant.pagination_default_page_size
def get_paginated_response(self, data):

View File

@@ -180,10 +180,10 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
)
def _filter_applications_with_launch_url(
self, applications: QuerySet[Application]
self, paginated_apps: QuerySet[Application]
) -> list[Application]:
applications = []
for app in applications:
for app in paginated_apps:
if app.get_launch_url():
applications.append(app)
return applications

View File

@@ -33,6 +33,16 @@ from authentik.endpoints.connectors.agent.auth import AgentAuth
from authentik.rbac.api.roles import RoleSerializer
from authentik.rbac.decorators import permission_required
PARTIAL_USER_SERIALIZER_MODEL_FIELDS = [
"pk",
"username",
"name",
"is_active",
"last_login",
"email",
"attributes",
]
class PartialUserSerializer(ModelSerializer):
"""Partial User Serializer, does not include child relations."""
@@ -42,16 +52,7 @@ class PartialUserSerializer(ModelSerializer):
class Meta:
model = User
fields = [
"pk",
"username",
"name",
"is_active",
"last_login",
"email",
"attributes",
"uid",
]
fields = PARTIAL_USER_SERIALIZER_MODEL_FIELDS + ["uid"]
class RelatedGroupSerializer(ModelSerializer):
@@ -262,7 +263,14 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
base_qs = Group.objects.all().prefetch_related("roles")
if self.serializer_class(context={"request": self.request})._should_include_users:
base_qs = base_qs.prefetch_related("users")
# Only fetch fields needed by PartialUserSerializer to reduce DB load and instantiation
# time
base_qs = base_qs.prefetch_related(
Prefetch(
"users",
queryset=User.objects.all().only(*PARTIAL_USER_SERIALIZER_MODEL_FIELDS),
)
)
else:
base_qs = base_qs.prefetch_related(
Prefetch("users", queryset=User.objects.all().only("id"))

View File

@@ -4,7 +4,6 @@ from typing import Any
from django.utils.timezone import now
from drf_spectacular.utils import OpenApiResponse, extend_schema
from guardian.shortcuts import get_anonymous_user
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField
@@ -145,12 +144,6 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
owner_field = "user"
rbac_allow_create_without_perm = True
def get_queryset(self):
user = self.request.user if self.request else get_anonymous_user()
if user.is_superuser:
return super().get_queryset()
return super().get_queryset().filter(user=user.pk)
def perform_create(self, serializer: TokenSerializer):
if not self.request.user.is_superuser:
instance = serializer.save(

View File

@@ -18,10 +18,9 @@ def migrate_object_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
RoleModelPermission = apps.get_model("guardian", "RoleModelPermission")
def get_role_for_user_id(user_id: int) -> Role:
name = f"ak-managed-role--user-{user_id}"
name = f"ak-migrated-role--user-{user_id}"
role, created = Role.objects.using(db_alias).get_or_create(
name=name,
managed=name,
)
if created:
role.users.add(user_id)
@@ -32,11 +31,10 @@ def migrate_object_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
if not role:
# Every django group should already have a role, so this should never happen.
# But let's be nice.
name = f"ak-managed-role--group-{group_id}"
name = f"ak-migrated-role--group-{group_id}"
role, created = Role.objects.using(db_alias).get_or_create(
group_id=group_id,
name=name,
managed=name,
)
if created:
role.group_id = group_id

View File

@@ -183,16 +183,16 @@ class TestTokenAPI(APITestCase):
self.assertEqual(len(body["results"]), 1)
self.assertEqual(body["results"][0]["identifier"], token_should.identifier)
def test_list_admin(self):
"""Test Token List (Test with admin auth)"""
def test_list_with_permission(self):
"""Test Token List (Test with `view_token` permission)"""
Token.objects.all().delete()
self.client.force_login(self.admin)
token_should: Token = Token.objects.create(
identifier="test", expiring=False, user=self.user
)
token_should_not: Token = Token.objects.create(
identifier="test-2", expiring=False, user=get_anonymous_user()
)
self.user.assign_perms_to_managed_role("authentik_core.view_token")
response = self.client.get(reverse("authentik_api:token-list"))
body = loads(response.content)
self.assertEqual(len(body["results"]), 2)

View File

@@ -1,7 +1,5 @@
"""Crypto API Views"""
from datetime import datetime
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import load_pem_x509_certificate
@@ -15,14 +13,12 @@ from drf_spectacular.utils import (
OpenApiParameter,
OpenApiResponse,
extend_schema,
extend_schema_field,
)
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import (
CharField,
ChoiceField,
DateTimeField,
IntegerField,
SerializerMethodField,
)
@@ -51,59 +47,15 @@ LOGGER = get_logger()
class CertificateKeyPairSerializer(ModelSerializer):
"""CertificateKeyPair Serializer"""
fingerprint_sha256 = SerializerMethodField()
fingerprint_sha1 = SerializerMethodField()
cert_expiry = SerializerMethodField()
cert_subject = SerializerMethodField()
private_key_available = SerializerMethodField()
key_type = SerializerMethodField()
certificate_download_url = SerializerMethodField()
private_key_download_url = SerializerMethodField()
@property
def _should_include_details(self) -> bool:
request: Request = self.context.get("request", None)
if not request:
return True
return str(request.query_params.get("include_details", "true")).lower() == "true"
def get_fingerprint_sha256(self, instance: CertificateKeyPair) -> str | None:
"Get certificate Hash (SHA256)"
if not self._should_include_details:
return None
return instance.fingerprint_sha256
def get_fingerprint_sha1(self, instance: CertificateKeyPair) -> str | None:
"Get certificate Hash (SHA1)"
if not self._should_include_details:
return None
return instance.fingerprint_sha1
def get_cert_expiry(self, instance: CertificateKeyPair) -> datetime | None:
"Get certificate expiry"
if not self._should_include_details:
return None
return DateTimeField().to_representation(instance.certificate.not_valid_after_utc)
def get_cert_subject(self, instance: CertificateKeyPair) -> str | None:
"""Get certificate subject as full rfc4514"""
if not self._should_include_details:
return None
return instance.certificate.subject.rfc4514_string()
def get_private_key_available(self, instance: CertificateKeyPair) -> bool:
"""Show if this keypair has a private key configured or not"""
return instance.key_data != "" and instance.key_data is not None
@extend_schema_field(ChoiceField(choices=KeyType.choices, allow_null=True))
def get_key_type(self, instance: CertificateKeyPair) -> str | None:
"""Get the key algorithm type from the certificate's public key"""
if not self._should_include_details:
return None
return instance.key_type
def get_certificate_download_url(self, instance: CertificateKeyPair) -> str:
"""Get URL to download certificate"""
return (
@@ -175,6 +127,11 @@ class CertificateKeyPairSerializer(ModelSerializer):
"managed": {"read_only": True},
"key_data": {"write_only": True},
"certificate_data": {"write_only": True},
"fingerprint_sha256": {"read_only": True},
"fingerprint_sha1": {"read_only": True},
"cert_expiry": {"read_only": True},
"cert_subject": {"read_only": True},
"key_type": {"read_only": True},
}
@@ -216,17 +173,12 @@ class CertificateKeyPairFilter(FilterSet):
return queryset.exclude(key_data__exact="")
def filter_key_type(self, queryset, name, value): # pragma: no cover
"""Filter certificates by key type using the public key from the certificate"""
"""Filter certificates by key type using the stored database field"""
if not value:
return queryset
# value is a list of KeyType enum values from MultipleChoiceFilter
filtered_pks = []
for cert in queryset:
if cert.key_type in value:
filtered_pks.append(cert.pk)
return queryset.filter(pk__in=filtered_pks)
return queryset.filter(key_type__in=value)
class Meta:
model = CertificateKeyPair
@@ -263,7 +215,6 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
"Can be specified multiple times (e.g. '?key_type=rsa&key_type=ec')"
),
),
OpenApiParameter("include_details", bool, default=True),
]
)
def list(self, request, *args, **kwargs):

View File

@@ -0,0 +1,117 @@
# Generated by Django 5.2.9 on 2025-12-09 06:22
from hashlib import md5
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate
from django.db import migrations, models
from authentik.crypto.signals import extract_certificate_metadata
def backfill_certificate_metadata(apps, schema_editor): # noqa: ARG001
"""Backfill certificate metadata and kid for existing records."""
CertificateKeyPair = apps.get_model("authentik_crypto", "CertificateKeyPair")
for cert in CertificateKeyPair.objects.all():
updated_fields = []
if cert.certificate_data:
try:
certificate = load_pem_x509_certificate(
cert.certificate_data.encode("utf-8"), default_backend()
)
metadata = extract_certificate_metadata(certificate)
cert.key_type = metadata["key_type"]
cert.cert_expiry = metadata["cert_expiry"]
cert.cert_subject = metadata["cert_subject"]
cert.fingerprint_sha256 = metadata["fingerprint_sha256"]
cert.fingerprint_sha1 = metadata["fingerprint_sha1"]
updated_fields.extend(
[
"key_type",
"cert_expiry",
"cert_subject",
"fingerprint_sha256",
"fingerprint_sha1",
]
)
except (ValueError, TypeError, AttributeError):
pass
# Backfill kid with MD5 for backwards compatibility
if cert.key_data:
cert.kid = md5(cert.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
updated_fields.append("kid")
if updated_fields:
cert.save(update_fields=updated_fields)
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0005_alter_certificatekeypair_options"),
]
operations = [
migrations.AddField(
model_name="certificatekeypair",
name="cert_expiry",
field=models.DateTimeField(blank=True, help_text="Certificate expiry date", null=True),
),
migrations.AddField(
model_name="certificatekeypair",
name="cert_subject",
field=models.TextField(
blank=True, help_text="Certificate subject as RFC4514 string", null=True
),
),
migrations.AddField(
model_name="certificatekeypair",
name="fingerprint_sha1",
field=models.CharField(
blank=True,
help_text="SHA1 fingerprint of the certificate",
max_length=59,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="fingerprint_sha256",
field=models.CharField(
blank=True,
help_text="SHA256 fingerprint of the certificate",
max_length=95,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="key_type",
field=models.CharField(
blank=True,
choices=[
("rsa", "RSA"),
("ec", "Elliptic Curve"),
("dsa", "DSA"),
("ed25519", "Ed25519"),
("ed448", "Ed448"),
],
help_text="Key algorithm type detected from the certificate's public key",
max_length=16,
null=True,
),
),
migrations.AddField(
model_name="certificatekeypair",
name="kid",
field=models.CharField(
blank=True, help_text="Key ID generated from private key", max_length=128, null=True
),
),
migrations.RunPython(backfill_certificate_metadata, migrations.RunPython.noop),
]

View File

@@ -1,7 +1,8 @@
"""authentik crypto models"""
from base64 import urlsafe_b64encode
from binascii import hexlify
from hashlib import md5
from hashlib import md5, sha512
from ssl import PEM_FOOTER, PEM_HEADER
from textwrap import wrap
from uuid import uuid4
@@ -47,6 +48,39 @@ def fingerprint_sha256(cert: Certificate) -> str:
return hexlify(cert.fingerprint(hashes.SHA256()), ":").decode("utf-8")
def detect_key_type(certificate: Certificate) -> str | None:
"""Detect the key algorithm type by parsing the certificate's public key"""
try:
public_key = certificate.public_key()
if isinstance(public_key, RSAPublicKey):
return KeyType.RSA
if isinstance(public_key, EllipticCurvePublicKey):
return KeyType.EC
if isinstance(public_key, DSAPublicKey):
return KeyType.DSA
if isinstance(public_key, Ed25519PublicKey):
return KeyType.ED25519
if isinstance(public_key, Ed448PublicKey):
return KeyType.ED448
except (ValueError, TypeError, AttributeError) as exc:
LOGGER.warning("Failed to detect key type", exc=exc)
return None
def generate_key_id(key_data: str) -> str:
"""Generate Key ID using SHA512 + urlsafe_b64encode."""
if not key_data:
return ""
return urlsafe_b64encode(sha512(key_data.encode("utf-8")).digest()).decode("utf-8").rstrip("=")
def generate_key_id_legacy(key_data: str) -> str:
"""Generate Key ID using MD5 (legacy format for backwards compatibility)."""
if not key_data:
return ""
return md5(key_data.encode("utf-8")).hexdigest() # nosec
class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""CertificateKeyPair that can be used for signing or encrypting if `key_data`
is set, otherwise it can be used to verify remote data."""
@@ -62,6 +96,41 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
blank=True,
default="",
)
key_type = models.CharField(
max_length=16,
choices=KeyType.choices,
null=True,
blank=True,
help_text=_("Key algorithm type detected from the certificate's public key"),
)
cert_expiry = models.DateTimeField(
null=True,
blank=True,
help_text=_("Certificate expiry date"),
)
cert_subject = models.TextField(
null=True,
blank=True,
help_text=_("Certificate subject as RFC4514 string"),
)
fingerprint_sha256 = models.CharField(
max_length=95,
null=True,
blank=True,
help_text=_("SHA256 fingerprint of the certificate"),
)
fingerprint_sha1 = models.CharField(
max_length=59,
null=True,
blank=True,
help_text=_("SHA1 fingerprint of the certificate"),
)
kid = models.CharField(
max_length=128,
null=True,
blank=True,
help_text=_("Key ID generated from private key"),
)
_cert: Certificate | None = None
_private_key: PrivateKeyTypes | None = None
@@ -106,41 +175,6 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
return None
return self._private_key
@property
def fingerprint_sha256(self) -> str:
"""Get SHA256 Fingerprint of certificate_data"""
return fingerprint_sha256(self.certificate)
@property
def fingerprint_sha1(self) -> str:
"""Get SHA1 Fingerprint of certificate_data"""
return hexlify(self.certificate.fingerprint(hashes.SHA1()), ":").decode("utf-8") # nosec
@property
def kid(self):
"""Get Key ID used for JWKS"""
return (
md5(self.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
if self.key_data
else ""
) # nosec
@property
def key_type(self) -> str | None:
"""Get the key algorithm type from the certificate's public key"""
public_key = self.certificate.public_key()
if isinstance(public_key, RSAPublicKey):
return KeyType.RSA
if isinstance(public_key, EllipticCurvePublicKey):
return KeyType.EC
if isinstance(public_key, DSAPublicKey):
return KeyType.DSA
if isinstance(public_key, Ed25519PublicKey):
return KeyType.ED25519
if isinstance(public_key, Ed448PublicKey):
return KeyType.ED448
return None
def __str__(self) -> str:
return f"Certificate-Key Pair {self.name}"

View File

@@ -0,0 +1,70 @@
"""authentik crypto signals"""
from binascii import hexlify
from datetime import datetime
from ssl import CertificateError
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import Certificate
from django.db.models.signals import pre_save
from django.dispatch import receiver
from structlog.stdlib import get_logger
from authentik.crypto.models import (
CertificateKeyPair,
detect_key_type,
fingerprint_sha256,
generate_key_id,
generate_key_id_legacy,
)
LOGGER = get_logger()
def extract_certificate_metadata(certificate: Certificate) -> dict[str, str | datetime]:
"""Extract all metadata fields from a certificate."""
metadata = {}
try:
metadata["key_type"] = detect_key_type(certificate)
metadata["cert_expiry"] = certificate.not_valid_after_utc
metadata["cert_subject"] = certificate.subject.rfc4514_string()
metadata["fingerprint_sha256"] = fingerprint_sha256(certificate)
metadata["fingerprint_sha1"] = hexlify(
certificate.fingerprint(hashes.SHA1()), ":" # nosec
).decode("utf-8")
except (ValueError, TypeError, AttributeError) as exc:
raise CertificateError(f"Invalid certificate metadata: {exc}") from exc
return metadata
@receiver(pre_save, sender="authentik_crypto.CertificateKeyPair")
def certificate_key_pair_pre_save(
sender: type[CertificateKeyPair], instance: CertificateKeyPair, **_
):
"""Automatically populate certificate metadata fields before saving"""
# Only extract metadata if certificate_data is present
if not instance.certificate_data:
return
try:
metadata = extract_certificate_metadata(instance.certificate)
except (CertificateError, ValueError, TypeError, AttributeError) as exc:
LOGGER.warning("Failed to extract certificate metadata", exc=exc)
return
instance.key_type = metadata["key_type"]
instance.cert_expiry = metadata["cert_expiry"]
instance.cert_subject = metadata["cert_subject"]
instance.fingerprint_sha256 = metadata["fingerprint_sha256"]
instance.fingerprint_sha1 = metadata["fingerprint_sha1"]
# Generate kid if not set, or regenerate if key_data has changed
# Preserve existing kid (MD5 or SHA512) if it matches the current key_data
if instance.key_data:
new_kid = generate_key_id(instance.key_data)
legacy_kid = generate_key_id_legacy(instance.key_data)
if instance.kid not in (new_kid, legacy_kid):
instance.kid = new_kid

View File

@@ -20,7 +20,7 @@ from authentik.core.tests.utils import (
)
from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair
from authentik.crypto.models import CertificateKeyPair, generate_key_id, generate_key_id_legacy
from authentik.crypto.tasks import MANAGED_DISCOVERED, certificate_discovery
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id, generate_key
@@ -173,21 +173,24 @@ class TestCrypto(APITestCase):
self.assertEqual(api_cert["fingerprint_sha1"], cert.fingerprint_sha1)
self.assertEqual(api_cert["fingerprint_sha256"], cert.fingerprint_sha256)
def test_list_without_details(self):
"""Test API List (no details)"""
def test_list_always_includes_details(self):
"""Test API List always includes certificate details"""
cert = create_test_cert()
self.client.force_login(create_test_admin_user())
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-list",
),
data={"name": cert.name, "include_details": False},
data={"name": cert.name},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
api_cert = [x for x in body["results"] if x["name"] == cert.name][0]
self.assertEqual(api_cert["fingerprint_sha1"], None)
self.assertEqual(api_cert["fingerprint_sha256"], None)
# All details should now always be included
self.assertEqual(api_cert["fingerprint_sha1"], cert.fingerprint_sha1)
self.assertEqual(api_cert["fingerprint_sha256"], cert.fingerprint_sha256)
self.assertIsNotNone(api_cert["cert_expiry"])
self.assertIsNotNone(api_cert["cert_subject"])
def test_certificate_download(self):
"""Test certificate export (download)"""
@@ -426,3 +429,114 @@ class TestCrypto(APITestCase):
self.assertEqual(
1, final_count, "Should not create duplicate cert for same private key"
)
def test_metadata_extraction_with_cert_and_key(self):
"""Test that metadata is extracted when creating keypair with certificate and key"""
cert = create_test_cert()
# Verify all metadata fields are populated
self.assertIsNotNone(cert.key_type)
self.assertIsNotNone(cert.cert_expiry)
self.assertIsNotNone(cert.cert_subject)
self.assertIsNotNone(cert.fingerprint_sha256)
self.assertIsNotNone(cert.fingerprint_sha1)
# Verify kid is generated using SHA512 for new records
self.assertIsNotNone(cert.kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))
def test_metadata_extraction_without_key(self):
"""Test that metadata is extracted when creating keypair without private key"""
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
# Create keypair with only certificate, no key
cert = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data=builder.certificate,
key_data="",
)
# Verify certificate metadata fields are populated
self.assertIsNotNone(cert.key_type)
self.assertIsNotNone(cert.cert_expiry)
self.assertIsNotNone(cert.cert_subject)
self.assertIsNotNone(cert.fingerprint_sha256)
self.assertIsNotNone(cert.fingerprint_sha1)
# Verify kid is empty when no key_data
self.assertEqual(cert.kid, None)
def test_metadata_extraction_invalid_cert(self):
"""Test that invalid certificate data doesn't crash, just skips metadata"""
cert = CertificateKeyPair.objects.create(
name=generate_id(),
certificate_data="invalid certificate data",
key_data="",
)
# Verify metadata fields are None for invalid cert
self.assertIsNone(cert.key_type)
self.assertIsNone(cert.cert_expiry)
self.assertIsNone(cert.cert_subject)
self.assertIsNone(cert.fingerprint_sha256)
self.assertIsNone(cert.fingerprint_sha1)
self.assertIsNone(cert.kid)
def test_kid_legacy_preservation(self):
"""Test that legacy MD5 kid is preserved when key_data hasn't changed"""
cert = create_test_cert()
# Simulate a legacy MD5 kid (as if backfilled from old system)
legacy_kid = generate_key_id_legacy(cert.key_data)
CertificateKeyPair.objects.filter(pk=cert.pk).update(kid=legacy_kid)
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
# Save the cert again (e.g., name change) - kid should be preserved
cert.name = generate_id()
cert.save()
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
def test_kid_regenerated_on_key_change(self):
"""Test that kid is regenerated when key_data changes"""
cert = create_test_cert()
original_kid = cert.kid
# Generate a new key and update the keypair
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
cert.key_data = builder.private_key
cert.certificate_data = builder.certificate
cert.save()
cert.refresh_from_db()
# Kid should be regenerated for the new key
self.assertNotEqual(cert.kid, original_kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))
def test_kid_regenerated_on_key_change_from_legacy(self):
"""Test that kid is regenerated from legacy MD5 when key_data changes"""
cert = create_test_cert()
# Simulate a legacy MD5 kid
legacy_kid = generate_key_id_legacy(cert.key_data)
CertificateKeyPair.objects.filter(pk=cert.pk).update(kid=legacy_kid)
cert.refresh_from_db()
self.assertEqual(cert.kid, legacy_kid)
# Generate a new key and update the keypair
builder = CertificateBuilder(generate_id())
builder.build(subject_alt_names=[], validity_days=3)
cert.key_data = builder.private_key
cert.certificate_data = builder.certificate
cert.save()
cert.refresh_from_db()
# Kid should now be SHA512 for the new key
self.assertNotEqual(cert.kid, legacy_kid)
self.assertEqual(cert.kid, generate_key_id(cert.key_data))

18
authentik/flows/auth.py Normal file
View File

@@ -0,0 +1,18 @@
from typing import cast
from django.contrib.auth.models import AnonymousUser
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
class FlowActive(BaseAuthentication):
"""Authenticate requests when a flow is currently active"""
def authenticate(self, request: Request):
plan = cast(FlowPlan | None, request.session.get(SESSION_KEY_PLAN))
if not plan:
return None
return (plan.context.get(PLAN_CONTEXT_PENDING_USER, AnonymousUser()), plan)

View File

@@ -249,7 +249,7 @@ class ChallengeStageView(StageView):
"f(ch): invalid challenge response",
errors=challenge_response.errors,
)
return HttpChallengeResponse(challenge_response)
return HttpChallengeResponse(challenge_response, status=400)
class AccessDeniedStage(ChallengeStageView):

View File

@@ -48,6 +48,9 @@ class FlowTestCase(APITestCase):
self.assertEqual(raw_response[key], expected)
return raw_response
def get_flow_plan(self) -> FlowPlan | None:
return self.client.session.get(SESSION_KEY_PLAN)
def assertStageRedirects(self, response: HttpResponse, to: str) -> dict[str, Any]:
"""Wrapper around assertStageResponse that checks for a redirect"""
return self.assertStageResponse(response, component="xak-flow-redirect", to=to)

View File

@@ -147,6 +147,8 @@ class FlowExecutorView(APIView):
token.delete()
if not isinstance(plan, FlowPlan):
return None
if existing_plan := self.request.session[SESSION_KEY_PLAN]:
plan.context.update(existing_plan.context)
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
self._logger.debug("f(exec): restored flow plan from token", plan=plan)
return plan
@@ -256,6 +258,11 @@ class FlowExecutorView(APIView):
serializers=challenge_types,
resource_type_field_name="component",
),
400: PolymorphicProxySerializer(
component_name="ChallengeTypes",
serializers=challenge_types,
resource_type_field_name="component",
),
},
request=OpenApiTypes.NONE,
parameters=[
@@ -303,6 +310,11 @@ class FlowExecutorView(APIView):
serializers=challenge_types,
resource_type_field_name="component",
),
400: PolymorphicProxySerializer(
component_name="ChallengeTypes",
serializers=challenge_types,
resource_type_field_name="component",
),
},
request=PolymorphicProxySerializer(
component_name="FlowChallengeResponse",

View File

@@ -86,7 +86,7 @@ class OutpostConfig:
class OutpostModel(Model):
"""Base model for providers that need more objects than just themselves"""
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
"""Return a list of all required objects"""
return [self]
@@ -332,41 +332,35 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
"""Create per-object and global permissions for outpost service-account"""
# To ensure the user only has the correct permissions, we delete all of them and re-add
# the ones the user needs
with transaction.atomic():
user.remove_all_perms_from_managed_role()
for model_or_perm in self.get_required_objects():
if isinstance(model_or_perm, models.Model):
model_or_perm: models.Model
code_name = (
f"{model_or_perm._meta.app_label}.view_{model_or_perm._meta.model_name}"
)
try:
user.assign_perms_to_managed_role(code_name, model_or_perm)
except (Permission.DoesNotExist, AttributeError) as exc:
LOGGER.warning(
"permission doesn't exist",
code_name=code_name,
user=user,
model=model_or_perm,
try:
with transaction.atomic():
user.remove_all_perms_from_managed_role()
for model_or_perm in self.get_required_objects():
if isinstance(model_or_perm, models.Model):
code_name = (
f"{model_or_perm._meta.app_label}.view_{model_or_perm._meta.model_name}"
)
Event.new(
action=EventAction.SYSTEM_EXCEPTION,
message=(
"While setting the permissions for the service-account, a "
"permission was not found: Check "
"https://docs.goauthentik.io/troubleshooting/missing_permission"
),
).with_exception(exc).set_user(user).save()
else:
app_label, perm = model_or_perm.split(".")
permission = Permission.objects.filter(
codename=perm,
content_type__app_label=app_label,
)
if not permission.exists():
LOGGER.warning("permission doesn't exist", perm=model_or_perm)
continue
user.assign_perms_to_managed_role(permission.first())
user.assign_perms_to_managed_role(code_name, model_or_perm)
elif isinstance(model_or_perm, tuple):
perm, obj = model_or_perm
user.assign_perms_to_managed_role(perm, obj)
else:
user.assign_perms_to_managed_role(model_or_perm)
except (Permission.DoesNotExist, AttributeError) as exc:
LOGGER.warning(
"permission doesn't exist",
code_name=code_name,
user=user,
model=model_or_perm,
)
Event.new(
action=EventAction.SYSTEM_EXCEPTION,
message=(
"While setting the permissions for the service-account, a "
"permission was not found: Check "
"https://docs.goauthentik.io/troubleshooting/missing_permission"
),
).with_exception(exc).set_user(user).save()
LOGGER.debug(
"Updated service account's permissions",
obj_perms=user.get_all_obj_perms_on_managed_role(),
@@ -431,7 +425,7 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
Token.objects.filter(identifier=self.token_identifier).delete()
return self.token
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
"""Get an iterator of all objects the user needs read access to"""
objects: list[models.Model | str] = [
self,
@@ -445,7 +439,9 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
if self.managed:
for brand in Brand.objects.filter(web_certificate__isnull=False):
objects.append(brand)
objects.append(brand.web_certificate)
objects.append(("view_certificatekeypair", brand.web_certificate))
objects.append(("view_certificatekeypair_certificate", brand.web_certificate))
objects.append(("view_certificatekeypair_key", brand.web_certificate))
return objects
def __str__(self) -> str:

View File

@@ -51,10 +51,12 @@ class OutpostTests(TestCase):
permissions = outpost.user.get_all_obj_perms_on_managed_role().order_by(
"content_type__model"
)
self.assertEqual(len(permissions), 3)
self.assertEqual(len(permissions), 5)
self.assertEqual(permissions[0].object_pk, str(keypair.pk))
self.assertEqual(permissions[1].object_pk, str(outpost.pk))
self.assertEqual(permissions[2].object_pk, str(provider.pk))
self.assertEqual(permissions[1].object_pk, str(keypair.pk))
self.assertEqual(permissions[2].object_pk, str(keypair.pk))
self.assertEqual(permissions[3].object_pk, str(outpost.pk))
self.assertEqual(permissions[4].object_pk, str(provider.pk))
# Remove provider from outpost, user should only have access to outpost
outpost.providers.remove(provider)

View File

@@ -93,11 +93,13 @@ class LDAPProvider(OutpostModel, BackchannelProvider):
def __str__(self):
return f"LDAP Provider {self.name}"
def get_required_objects(self) -> Iterable[models.Model | str]:
required_models = [self, "authentik_core.view_user", "authentik_core.view_group"]
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self, "authentik_core.view_user", "authentik_core.view_group"]
if self.certificate is not None:
required_models.append(self.certificate)
return required_models
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
class Meta:
verbose_name = _("LDAP Provider")

View File

@@ -179,11 +179,13 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
def __str__(self):
return f"Proxy Provider {self.name}"
def get_required_objects(self) -> Iterable[models.Model | str]:
required_models = [self]
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self]
if self.certificate is not None:
required_models.append(self.certificate)
return required_models
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
class Meta:
verbose_name = _("Proxy Provider")

View File

@@ -1,10 +1,14 @@
"""proxy provider tests"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.lib.generators import generate_id
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.oauth2.models import ClientTypes
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
@@ -127,3 +131,55 @@ class ProxyProviderTests(APITestCase):
self.assertEqual(response.status_code, 200)
provider: ProxyProvider = ProxyProvider.objects.get(name=name)
self.assertEqual(provider.client_type, ClientTypes.CONFIDENTIAL)
def test_sa_fetch(self):
"""Test fetching the outpost config as the service account"""
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
provider = ProxyProvider.objects.create(name=generate_id())
Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
outpost.providers.add(provider)
res = self.client.get(
reverse("authentik_api:proxyprovideroutpost-list"),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
body = loads(res.content)
self.assertEqual(body["pagination"]["count"], 1)
def test_sa_perms_cert(self):
"""Test permissions to access a configured certificate"""
cert = create_test_cert()
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
provider = ProxyProvider.objects.create(name=generate_id(), certificate=cert)
Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
outpost.providers.add(provider)
res = self.client.get(
reverse("authentik_api:proxyprovideroutpost-list"),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
body = loads(res.content)
self.assertEqual(body["pagination"]["count"], 1)
cert_id = body["results"][0]["certificate"]
self.assertEqual(cert_id, str(cert.pk))
res = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={
"pk": cert_id,
},
),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
self.assertEqual(res.status_code, 200)
# res = self.client.get(
# reverse(
# "authentik_api:certificatekeypair-view-private-key",
# kwargs={
# "pk": cert_id,
# },
# ),
# HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
# )
# self.assertEqual(res.status_code, 200)

View File

@@ -64,10 +64,12 @@ class RadiusProvider(OutpostModel, Provider):
return RadiusProviderSerializer
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self, "authentik_stages_mtls.pass_outpost_certificate"]
if self.certificate is not None:
required.append(self.certificate)
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
def __str__(self):

View File

@@ -9,7 +9,7 @@ from guardian.shortcuts import get_objects_for_user
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.fields import CharField, ChoiceField, IntegerField
from rest_framework.permissions import IsAuthenticated
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ModelViewSet
@@ -21,9 +21,11 @@ from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import ModelSerializer
from authentik.core.models import User
from authentik.flows.api.stages import StageSerializer
from authentik.flows.auth import FlowActive
from authentik.flows.planner import FlowPlan
from authentik.rbac.decorators import permission_required
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_duo.stage import SESSION_KEY_DUO_ENROLL
from authentik.stages.authenticator_duo.stage import PLAN_CONTEXT_DUO_ENROLL
LOGGER = get_logger()
@@ -84,14 +86,20 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
),
},
)
@action(methods=["POST"], detail=True, permission_classes=[IsAuthenticated])
@action(
methods=["POST"],
detail=True,
authentication_classes=[FlowActive],
permission_classes=[AllowAny],
)
def enrollment_status(self, request: Request, pk: str) -> Response:
"""Check enrollment status of user details in current session"""
stage: AuthenticatorDuoStage = AuthenticatorDuoStage.objects.filter(pk=pk).first()
if not stage:
raise Http404
client = stage.auth_client()
enroll = self.request.session.get(SESSION_KEY_DUO_ENROLL)
plan: FlowPlan = request.auth
enroll = plan.context.get(PLAN_CONTEXT_DUO_ENROLL)
if not enroll:
return Response(status=400)
status = client.enroll_status(enroll["user_id"], enroll["activation_code"])

View File

@@ -14,7 +14,7 @@ from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import InvalidStageError
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
SESSION_KEY_DUO_ENROLL = "authentik/stages/authenticator_duo/enroll"
PLAN_CONTEXT_DUO_ENROLL = "goauthentik.io/stages/authenticator_duo/enroll"
class AuthenticatorDuoChallenge(WithUserInfoChallenge):
@@ -50,14 +50,14 @@ class AuthenticatorDuoStageView(ChallengeStageView):
user=user,
).from_http(self.request, user)
raise InvalidStageError(str(exc)) from exc
self.request.session[SESSION_KEY_DUO_ENROLL] = enroll
self.executor.plan.context[PLAN_CONTEXT_DUO_ENROLL] = enroll
return enroll
def get_challenge(self, *args, **kwargs) -> Challenge:
stage: AuthenticatorDuoStage = self.executor.current_stage
if SESSION_KEY_DUO_ENROLL not in self.request.session:
if PLAN_CONTEXT_DUO_ENROLL not in self.executor.plan.context:
self.duo_enroll()
enroll = self.request.session[SESSION_KEY_DUO_ENROLL]
enroll = self.executor.plan.context[PLAN_CONTEXT_DUO_ENROLL]
return AuthenticatorDuoChallenge(
data={
"activation_barcode": enroll["activation_barcode"],
@@ -69,14 +69,14 @@ class AuthenticatorDuoStageView(ChallengeStageView):
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
# Duo Challenge has already been validated
stage: AuthenticatorDuoStage = self.executor.current_stage
enroll = self.request.session.get(SESSION_KEY_DUO_ENROLL)
enroll = self.executor.plan.context.get(PLAN_CONTEXT_DUO_ENROLL)
enroll_status = stage.auth_client().enroll_status(
enroll["user_id"], enroll["activation_code"]
)
if enroll_status != "success":
return self.executor.stage_invalid(f"Invalid enrollment status: {enroll_status}.")
existing_device = DuoDevice.objects.filter(duo_user_id=enroll["user_id"]).first()
self.request.session.pop(SESSION_KEY_DUO_ENROLL)
self.executor.plan.context.pop(PLAN_CONTEXT_DUO_ENROLL)
if not existing_device:
DuoDevice.objects.create(
name="Duo Authenticator",
@@ -88,6 +88,3 @@ class AuthenticatorDuoStageView(ChallengeStageView):
else:
return self.executor.stage_invalid("Device with Credential ID already exists.")
return self.executor.stage_ok()
def cleanup(self):
self.request.session.pop(SESSION_KEY_DUO_ENROLL, None)

View File

@@ -11,7 +11,6 @@ from authentik.flows.models import FlowStageBinding
from authentik.flows.tests import FlowTestCase
from authentik.lib.generators import generate_id
from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice
from authentik.stages.authenticator_duo.stage import SESSION_KEY_DUO_ENROLL
from authentik.stages.identification.models import IdentificationStage, UserFields
@@ -51,42 +50,6 @@ class AuthenticatorDuoStageTests(FlowTestCase):
)
self.assertEqual(response.status_code, 404)
def test_api_enrollment(self):
"""Test `enrollment_status`"""
self.client.force_login(self.user)
stage = AuthenticatorDuoStage.objects.create(
name=generate_id(),
client_id=generate_id(),
client_secret=generate_id(),
api_hostname=generate_id(),
)
response = self.client.post(
reverse(
"authentik_api:authenticatorduostage-enrollment-status",
kwargs={
"pk": str(stage.pk),
},
)
)
self.assertEqual(response.status_code, 400)
session = self.client.session
session[SESSION_KEY_DUO_ENROLL] = {"user_id": "foo", "activation_code": "bar"}
session.save()
with patch("duo_client.auth.Auth.enroll_status", MagicMock(return_value="foo")):
response = self.client.post(
reverse(
"authentik_api:authenticatorduostage-enrollment-status",
kwargs={
"pk": str(stage.pk),
},
)
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content.decode(), '{"duo_response":"foo"}')
def test_api_import_manual_invalid_username(self):
"""Test `import_device_manual`"""
self.client.force_login(self.user)
@@ -314,6 +277,17 @@ class AuthenticatorDuoStageTests(FlowTestCase):
self.assertEqual(enroll_mock.call_count, 1)
with patch("duo_client.auth.Auth.enroll_status", MagicMock(return_value="success")):
response = self.client.post(
reverse(
"authentik_api:authenticatorduostage-enrollment-status",
kwargs={
"pk": str(stage.pk),
},
)
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content, {"duo_response": "success"})
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), {}
)

View File

@@ -6,7 +6,7 @@ from django.http.request import QueryDict
from django.template.exceptions import TemplateSyntaxError
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, IntegerField
from rest_framework.fields import BooleanField, CharField
from authentik.events.models import Event, EventAction
from authentik.flows.challenge import (
@@ -26,7 +26,7 @@ from authentik.stages.email.tasks import send_mails
from authentik.stages.email.utils import TemplateEmailMessage
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
SESSION_KEY_EMAIL_DEVICE = "authentik/stages/authenticator_email/email_device"
PLAN_CONTEXT_EMAIL_DEVICE = "goauthentik.io/stages/authenticator_email/email_device"
PLAN_CONTEXT_EMAIL = "email"
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
@@ -47,7 +47,7 @@ class AuthenticatorEmailChallengeResponse(ChallengeResponse):
device: EmailDevice
code = IntegerField(required=False)
code = CharField(required=False)
email = CharField(required=False)
component = CharField(default="ak-stage-authenticator-email")
@@ -79,7 +79,7 @@ class AuthenticatorEmailStageView(ChallengeStageView):
if EmailDevice.objects.filter(Q(email=email), stage=stage.pk).exists():
raise ValidationError(_("Invalid email"))
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
device: EmailDevice = self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE]
try:
message = TemplateEmailMessage(
@@ -116,9 +116,9 @@ class AuthenticatorEmailStageView(ChallengeStageView):
self.logger.debug("got email from plan context")
return context.get(PLAN_CONTEXT_PROMPT, {}).get(PLAN_CONTEXT_EMAIL)
# Check device for email
if SESSION_KEY_EMAIL_DEVICE in self.request.session:
if PLAN_CONTEXT_EMAIL_DEVICE in self.executor.plan.context:
self.logger.debug("got email from device in session")
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
device: EmailDevice = self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE]
if device.email == "":
return None
return device.email
@@ -135,7 +135,7 @@ class AuthenticatorEmailStageView(ChallengeStageView):
def get_response_instance(self, data: QueryDict) -> ChallengeResponse:
response = super().get_response_instance(data)
response.device = self.request.session[SESSION_KEY_EMAIL_DEVICE]
response.device = self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE]
return response
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@@ -147,11 +147,11 @@ class AuthenticatorEmailStageView(ChallengeStageView):
return self.executor.stage_invalid(
_("The user already has an email address registered for MFA.")
)
if SESSION_KEY_EMAIL_DEVICE not in self.request.session:
if PLAN_CONTEXT_EMAIL_DEVICE not in self.executor.plan.context:
device = EmailDevice(user=user, confirmed=False, stage=stage, name="Email Device")
valid_secs: int = timedelta_from_string(stage.token_expiry).total_seconds()
device.generate_token(valid_secs=valid_secs, commit=False)
self.request.session[SESSION_KEY_EMAIL_DEVICE] = device
self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE] = device
if email := self._has_email():
device.email = email
try:
@@ -165,16 +165,16 @@ class AuthenticatorEmailStageView(ChallengeStageView):
self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).pop(
PLAN_CONTEXT_EMAIL, None
)
self.request.session.pop(SESSION_KEY_EMAIL_DEVICE, None)
self.executor.plan.context.pop(PLAN_CONTEXT_EMAIL_DEVICE, None)
self.logger.warning("failed to send email to pre-set address", exc=exc)
return self.get(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
"""Email Token is validated by challenge"""
device: EmailDevice = self.request.session[SESSION_KEY_EMAIL_DEVICE]
device: EmailDevice = self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE]
if not device.confirmed:
return self.challenge_invalid(response)
device.save()
del self.request.session[SESSION_KEY_EMAIL_DEVICE]
del self.executor.plan.context[PLAN_CONTEXT_EMAIL_DEVICE]
return self.executor.stage_ok()

View File

@@ -11,7 +11,7 @@ from django.template.exceptions import TemplateDoesNotExist
from django.urls import reverse
from django.utils.timezone import now
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_user
from authentik.core.tests.utils import create_test_flow, create_test_user
from authentik.flows.models import FlowStageBinding
from authentik.flows.tests import FlowTestCase
from authentik.lib.config import CONFIG
@@ -21,9 +21,7 @@ from authentik.stages.authenticator_email.api import (
EmailDeviceSerializer,
)
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage, EmailDevice
from authentik.stages.authenticator_email.stage import (
SESSION_KEY_EMAIL_DEVICE,
)
from authentik.stages.authenticator_email.stage import PLAN_CONTEXT_EMAIL_DEVICE
from authentik.stages.email.utils import TemplateEmailMessage
@@ -33,7 +31,7 @@ class TestAuthenticatorEmailStage(FlowTestCase):
def setUp(self):
super().setUp()
self.flow = create_test_flow()
self.user = create_test_admin_user()
self.user = create_test_user()
self.user_noemail = create_test_user(email="")
self.stage = AuthenticatorEmailStage.objects.create(
name="email-authenticator",
@@ -213,20 +211,26 @@ class TestAuthenticatorEmailStage(FlowTestCase):
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email"},
)
self.assertIn("email required", str(response.content))
self.assertStageResponse(
response,
self.flow,
response_errors={"non_field_errors": [{"code": "invalid", "string": "email required"}]},
)
# Test invalid code
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "code": "000000"},
)
self.assertIn("Code does not match", str(response.content))
self.assertStageResponse(
response,
self.flow,
response_errors={
"non_field_errors": [{"code": "invalid", "string": "Code does not match"}]
},
)
# Test valid code
self.client.force_login(self.user)
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
device = self.device
token = device.token
response = self.client.post(
@@ -285,8 +289,7 @@ class TestAuthenticatorEmailStage(FlowTestCase):
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
)
self.assertIn(SESSION_KEY_EMAIL_DEVICE, self.client.session)
device = self.client.session[SESSION_KEY_EMAIL_DEVICE]
device = self.get_flow_plan().context[PLAN_CONTEXT_EMAIL_DEVICE]
self.assertIsInstance(device, EmailDevice)
self.assertFalse(device.confirmed)
self.assertEqual(device.user, self.user)
@@ -294,8 +297,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
# Test device confirmation and cleanup
device.confirmed = True
device.email = "new_test@authentik.local" # Use a different email
self.client.session[SESSION_KEY_EMAIL_DEVICE] = device
self.client.session.save()
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
data={"component": "ak-stage-authenticator-email", "code": device.token},

View File

@@ -5,7 +5,7 @@ from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, IntegerField
from rest_framework.fields import BooleanField, CharField
from authentik.flows.challenge import (
Challenge,
@@ -20,7 +20,7 @@ from authentik.stages.authenticator_sms.models import (
)
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
SESSION_KEY_SMS_DEVICE = "authentik/stages/authenticator_sms/sms_device"
PLAN_CONTEXT_SMS_DEVICE = "goauthentik.io/stages/authenticator_sms/sms_device"
PLAN_CONTEXT_PHONE = "phone"
@@ -38,7 +38,7 @@ class AuthenticatorSMSChallengeResponse(ChallengeResponse):
device: SMSDevice
code = IntegerField(required=False)
code = CharField(required=False)
phone_number = CharField(required=False)
component = CharField(default="ak-stage-authenticator-sms")
@@ -70,7 +70,7 @@ class AuthenticatorSMSStageView(ChallengeStageView):
if SMSDevice.objects.filter(query, stage=stage.pk).exists():
raise ValidationError(_("Invalid phone number"))
# No code yet, but we have a phone number, so send a verification message
device: SMSDevice = self.request.session[SESSION_KEY_SMS_DEVICE]
device: SMSDevice = self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE]
stage.send(self.request, device.token, device)
def _has_phone_number(self) -> str | None:
@@ -78,9 +78,9 @@ class AuthenticatorSMSStageView(ChallengeStageView):
if PLAN_CONTEXT_PHONE in context.get(PLAN_CONTEXT_PROMPT, {}):
self.logger.debug("got phone number from plan context")
return context.get(PLAN_CONTEXT_PROMPT, {}).get(PLAN_CONTEXT_PHONE)
if SESSION_KEY_SMS_DEVICE in self.request.session:
if PLAN_CONTEXT_SMS_DEVICE in self.executor.plan.context:
self.logger.debug("got phone number from device in session")
device: SMSDevice = self.request.session[SESSION_KEY_SMS_DEVICE]
device: SMSDevice = self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE]
if device.phone_number == "":
return None
return device.phone_number
@@ -95,7 +95,7 @@ class AuthenticatorSMSStageView(ChallengeStageView):
def get_response_instance(self, data: QueryDict) -> ChallengeResponse:
response = super().get_response_instance(data)
response.device = self.request.session[SESSION_KEY_SMS_DEVICE]
response.device = self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE]
return response
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@@ -103,10 +103,10 @@ class AuthenticatorSMSStageView(ChallengeStageView):
stage: AuthenticatorSMSStage = self.executor.current_stage
if SESSION_KEY_SMS_DEVICE not in self.request.session:
if PLAN_CONTEXT_SMS_DEVICE not in self.executor.plan.context:
device = SMSDevice(user=user, confirmed=False, stage=stage, name="SMS Device")
device.generate_token(commit=False)
self.request.session[SESSION_KEY_SMS_DEVICE] = device
self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE] = device
if phone_number := self._has_phone_number():
device.phone_number = phone_number
try:
@@ -120,14 +120,14 @@ class AuthenticatorSMSStageView(ChallengeStageView):
self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}).pop(
PLAN_CONTEXT_PHONE, None
)
self.request.session.pop(SESSION_KEY_SMS_DEVICE, None)
self.executor.plan.context.pop(PLAN_CONTEXT_SMS_DEVICE, None)
self.logger.warning("failed to send SMS message to pre-set number", exc=exc)
return self.get(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
"""SMS Token is validated by challenge"""
device: SMSDevice = self.request.session[SESSION_KEY_SMS_DEVICE]
device: SMSDevice = self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE]
if not device.confirmed:
return self.challenge_invalid(response)
stage: AuthenticatorSMSStage = self.executor.current_stage
@@ -135,5 +135,5 @@ class AuthenticatorSMSStageView(ChallengeStageView):
self.logger.debug("Hashing number on device")
device.set_hashed_number()
device.save()
del self.request.session[SESSION_KEY_SMS_DEVICE]
del self.executor.plan.context[PLAN_CONTEXT_SMS_DEVICE]
return self.executor.stage_ok()

View File

@@ -18,7 +18,7 @@ from authentik.stages.authenticator_sms.models import (
SMSProviders,
hash_phone_number,
)
from authentik.stages.authenticator_sms.stage import PLAN_CONTEXT_PHONE, SESSION_KEY_SMS_DEVICE
from authentik.stages.authenticator_sms.stage import PLAN_CONTEXT_PHONE, PLAN_CONTEXT_SMS_DEVICE
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
@@ -125,7 +125,7 @@ class AuthenticatorSMSStageTests(FlowTestCase):
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
request_body = dict(parse_qsl(mocker.request_history[0].body))
device: SMSDevice = self.client.session[SESSION_KEY_SMS_DEVICE]
device: SMSDevice = self.get_flow_plan().context[PLAN_CONTEXT_SMS_DEVICE]
self.assertEqual(
request_body,
{

View File

@@ -5,7 +5,7 @@ from urllib.parse import quote
from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.utils.translation import gettext_lazy as _
from rest_framework.fields import CharField, IntegerField
from rest_framework.fields import CharField
from rest_framework.serializers import ValidationError
from authentik.flows.challenge import (
@@ -32,10 +32,10 @@ class AuthenticatorTOTPChallengeResponse(ChallengeResponse):
device: TOTPDevice
code = IntegerField()
code = CharField()
component = CharField(default="ak-stage-authenticator-totp")
def validate_code(self, code: int) -> int:
def validate_code(self, code: str) -> str:
"""Validate totp code"""
if not self.device:
raise ValidationError(_("Code does not match"))

View File

@@ -1,5 +1,6 @@
"""authentik consent stage"""
from hmac import compare_digest
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
@@ -23,7 +24,7 @@ PLAN_CONTEXT_CONSENT = "consent"
PLAN_CONTEXT_CONSENT_HEADER = "consent_header"
PLAN_CONTEXT_CONSENT_PERMISSIONS = "consent_permissions"
PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS = "consent_additional_permissions"
SESSION_KEY_CONSENT_TOKEN = "authentik/stages/consent/token" # nosec
PLAN_CONTEXT_CONSENT_TOKEN = "goauthentik.io/stages/consent/token" # nosec
class ConsentPermissionSerializer(PassiveSerializer):
@@ -50,7 +51,9 @@ class ConsentChallengeResponse(ChallengeResponse):
token = CharField(required=True)
def validate_token(self, token: str):
if token != self.stage.executor.request.session[SESSION_KEY_CONSENT_TOKEN]:
if not compare_digest(
token, self.stage.executor.plan.context.get(PLAN_CONTEXT_CONSENT_TOKEN, "")
):
raise ValidationError(_("Invalid consent token, re-showing prompt"))
return token
@@ -62,7 +65,7 @@ class ConsentStageView(ChallengeStageView):
def get_challenge(self) -> Challenge:
token = str(uuid4())
self.request.session[SESSION_KEY_CONSENT_TOKEN] = token
self.executor.plan.context[PLAN_CONTEXT_CONSENT_TOKEN] = token
data = {
"permissions": self.executor.plan.context.get(PLAN_CONTEXT_CONSENT_PERMISSIONS, []),
"additional_permissions": self.executor.plan.context.get(

View File

@@ -19,7 +19,7 @@ from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConse
from authentik.stages.consent.stage import (
PLAN_CONTEXT_CONSENT_HEADER,
PLAN_CONTEXT_CONSENT_PERMISSIONS,
SESSION_KEY_CONSENT_TOKEN,
PLAN_CONTEXT_CONSENT_TOKEN,
)
@@ -83,11 +83,10 @@ class TestConsentStage(FlowTestCase):
)
self.assertEqual(response.status_code, 200)
session = self.client.session
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{
"token": session[SESSION_KEY_CONSENT_TOKEN],
"token": self.get_flow_plan().context[PLAN_CONTEXT_CONSENT_TOKEN],
},
)
@@ -122,7 +121,7 @@ class TestConsentStage(FlowTestCase):
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{
"token": session[SESSION_KEY_CONSENT_TOKEN],
"token": self.get_flow_plan().context[PLAN_CONTEXT_CONSENT_TOKEN],
},
)
self.assertEqual(response.status_code, 200)

View File

@@ -19,7 +19,7 @@ from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_PLAN, FlowExecutorView
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id
from authentik.stages.consent.stage import SESSION_KEY_CONSENT_TOKEN
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TOKEN
from authentik.stages.email.models import EmailStage
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, EmailStageView
@@ -174,7 +174,7 @@ class TestEmailStage(FlowTestCase):
kwargs={"flow_slug": self.flow.slug},
),
data={
"token": self.client.session[SESSION_KEY_CONSENT_TOKEN],
"token": self.get_flow_plan().context[PLAN_CONTEXT_CONSENT_TOKEN],
},
follow=True,
)

View File

@@ -245,7 +245,10 @@ class WorkerStatusMiddleware(Middleware):
WorkerStatusMiddleware.keep(status)
except DB_ERRORS: # pragma: no cover
sleep(10)
pass
try:
connections.close_all()
except DB_ERRORS:
pass
@staticmethod
def keep(status: WorkerStatus):

View File

@@ -37,7 +37,7 @@ services:
- ${COMPOSE_PORT_HTTPS:-9443}:9443
restart: unless-stopped
volumes:
- ./media:/data/media
- ./data:/data
- ./custom-templates:/templates
worker:
command: worker
@@ -57,7 +57,7 @@ services:
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./media:/data/media
- ./data:/data
- ./certs:/certs
- ./custom-templates:/templates
volumes:

2
go.mod
View File

@@ -32,7 +32,7 @@ require (
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
github.com/wwt/guac v1.3.2
goauthentik.io/api/v3 v3.2025120.26
goauthentik.io/api/v3 v3.2026020.3
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0

4
go.sum
View File

@@ -214,8 +214,8 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
goauthentik.io/api/v3 v3.2025120.26 h1:2lTMtjCWtdOeQe7kwjpGUx39qUEpcxcxTirIqMvn0Os=
goauthentik.io/api/v3 v3.2025120.26/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
goauthentik.io/api/v3 v3.2026020.3 h1:CKtPyAQToPT2yF5odTTc+IfPLhYeVX9FbLMeVnFgZps=
goauthentik.io/api/v3 v3.2026020.3/go.mod h1:82lqAz4jxzl6Cg0YDbhNtvvTG2rm6605ZhdJFnbbsl8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:5d35fb8d28b9095d123b7d96095bbf3750ff18be0a87e5a21c9cffc4351fbf96 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:8e8f9c84609b6005af0a4a8227cee53d6226aab1c6dcb22daf5aeeb8b05480e1 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/ldap ./cmd/ldap
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:07f41ce3f15b2bb5eb5bcd4e6efc0cb42bb7e5609e7244f636da1a91166817ca
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:10dadf1df1337e8eb4218acef6a3027abebf0a155a95d792af736f85ab0e9588
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -38,14 +38,14 @@ function check_if_root {
chown -R authentik:authentik /media /certs "${PROMETHEUS_MULTIPROC_DIR}"
chmod ug+rwx /media
chmod ug+rx /certs
exec chpst -u authentik:$GROUP env HOME=/authentik PATH="$PATH" $1
exec chpst -u authentik:$GROUP env HOME=/authentik $1
}
function run_authentik {
if [[ -x "$(command -v authentik)" ]]; then
exec authentik $@
else
exec env GOFIPS140=latest CGO_ENABLED=1 go run -v ./cmd/server/ $@
exec go run -v ./cmd/server/ $@
fi
}

View File

@@ -9,7 +9,7 @@
"version": "0.0.0",
"license": "MIT",
"devDependencies": {
"aws-cdk": "^2.1034.0",
"aws-cdk": "^2.1100.1",
"cross-env": "^10.1.0"
},
"engines": {
@@ -25,9 +25,9 @@
"license": "MIT"
},
"node_modules/aws-cdk": {
"version": "2.1034.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1034.0.tgz",
"integrity": "sha512-YsIeXmMP/9eGml/eoPs64kHzNR0IVezzwuH0XrLOtUCjYNb80cmmjoCNsMn96u9rJOte1Yg3jitrHi1wTqXAqw==",
"version": "2.1100.1",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1100.1.tgz",
"integrity": "sha512-q2poFrQh90TK6eqeI0zznA8r1JkDI63WVOSqC7gFGo6qjQjAnvFk/utxHoNRgAC0RL0CLd19uCcHh3jfX9NiSg==",
"dev": true,
"license": "Apache-2.0",
"bin": {

View File

@@ -7,7 +7,7 @@
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
},
"devDependencies": {
"aws-cdk": "^2.1034.0",
"aws-cdk": "^2.1100.1",
"cross-env": "^10.1.0"
},
"engines": {

Binary file not shown.

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-12-12 15:51+0000\n"
"POT-Creation-Date: 2025-12-17 00:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -558,6 +558,30 @@ msgid ""
"encryption."
msgstr ""
#: authentik/crypto/models.py
msgid "Key algorithm type detected from the certificate's public key"
msgstr ""
#: authentik/crypto/models.py
msgid "Certificate expiry date"
msgstr ""
#: authentik/crypto/models.py
msgid "Certificate subject as RFC4514 string"
msgstr ""
#: authentik/crypto/models.py
msgid "SHA256 fingerprint of the certificate"
msgstr ""
#: authentik/crypto/models.py
msgid "SHA1 fingerprint of the certificate"
msgstr ""
#: authentik/crypto/models.py
msgid "Key ID generated from private key"
msgstr ""
#: authentik/crypto/models.py
msgid "Certificate-Key Pair"
msgstr ""

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -529,3 +529,7 @@ class _PostgresConsumer(Consumer):
conn.close()
except DATABASE_ERRORS:
pass
try:
connections.close_all()
except DATABASE_ERRORS:
pass

View File

@@ -17,7 +17,7 @@ COPY web .
RUN npm run build-proxy
# Stage 2: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:5d35fb8d28b9095d123b7d96095bbf3750ff18be0a87e5a21c9cffc4351fbf96 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:8e8f9c84609b6005af0a4a8227cee53d6226aab1c6dcb22daf5aeeb8b05480e1 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -47,7 +47,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/proxy ./cmd/proxy
# Stage 3: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:07f41ce3f15b2bb5eb5bcd4e6efc0cb42bb7e5609e7244f636da1a91166817ca
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:10dadf1df1337e8eb4218acef6a3027abebf0a155a95d792af736f85ab0e9588
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:5d35fb8d28b9095d123b7d96095bbf3750ff18be0a87e5a21c9cffc4351fbf96 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:8e8f9c84609b6005af0a4a8227cee53d6226aab1c6dcb22daf5aeeb8b05480e1 AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1
# Stage 1: Build
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:5d35fb8d28b9095d123b7d96095bbf3750ff18be0a87e5a21c9cffc4351fbf96 AS builder
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.5-trixie@sha256:8e8f9c84609b6005af0a4a8227cee53d6226aab1c6dcb22daf5aeeb8b05480e1 AS builder
ARG TARGETOS
ARG TARGETARCH
@@ -31,7 +31,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
go build -o /go/radius ./cmd/radius
# Stage 2: Run
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:07f41ce3f15b2bb5eb5bcd4e6efc0cb42bb7e5609e7244f636da1a91166817ca
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:10dadf1df1337e8eb4218acef6a3027abebf0a155a95d792af736f85ab0e9588
ARG VERSION
ARG GIT_BUILD_HASH

View File

@@ -4728,11 +4728,6 @@ paths:
schema:
type: boolean
description: Only return certificate-key pairs with keys
- in: query
name: include_details
schema:
type: boolean
default: true
- in: query
name: key_type
schema:
@@ -33586,7 +33581,8 @@ components:
minLength: 1
default: ak-stage-authenticator-email
code:
type: integer
type: string
minLength: 1
email:
type: string
minLength: 1
@@ -33833,7 +33829,8 @@ components:
minLength: 1
default: ak-stage-authenticator-sms
code:
type: integer
type: string
minLength: 1
phone_number:
type: string
minLength: 1
@@ -34107,7 +34104,8 @@ components:
minLength: 1
default: ak-stage-authenticator-totp
code:
type: integer
type: string
minLength: 1
required:
- code
AuthenticatorTOTPStage:
@@ -34992,25 +34990,25 @@ components:
type: string
fingerprint_sha256:
type: string
nullable: true
description: Get certificate Hash (SHA256)
readOnly: true
nullable: true
description: SHA256 fingerprint of the certificate
fingerprint_sha1:
type: string
nullable: true
description: Get certificate Hash (SHA1)
readOnly: true
nullable: true
description: SHA1 fingerprint of the certificate
cert_expiry:
type: string
format: date-time
nullable: true
description: Get certificate expiry
readOnly: true
nullable: true
description: Certificate expiry date
cert_subject:
type: string
nullable: true
description: Get certificate subject as full rfc4514
readOnly: true
nullable: true
description: Certificate subject as RFC4514 string
private_key_available:
type: boolean
description: Show if this keypair has a private key configured or not
@@ -35018,8 +35016,9 @@ components:
key_type:
allOf:
- $ref: '#/components/schemas/KeyTypeEnum'
nullable: true
readOnly: true
nullable: true
description: Key algorithm type detected from the certificate's public key
certificate_download_url:
type: string
description: Get URL to download certificate

View File

@@ -5,11 +5,11 @@ services:
restart: never
network_mode: none
volumes:
- ${LOCAL_PROJECT_DIR:-../../}:/local
- ../../:/local
gen:
image: docker.io/openapitools/openapi-generator-cli:v7.16.0
restart: never
network_mode: none
volumes:
- ${LOCAL_PROJECT_DIR:-../../}:/local
- ../../:/local

6
scripts/generate_docker_compose.py Normal file → Executable file
View File

@@ -1,3 +1,5 @@
#!/usr/bin/env python3
from yaml import safe_dump
from authentik import authentik_version
@@ -42,7 +44,7 @@ base = {
"image": authentik_image,
"ports": ["${COMPOSE_PORT_HTTP:-9000}:9000", "${COMPOSE_PORT_HTTPS:-9443}:9443"],
"restart": "unless-stopped",
"volumes": ["./media:/data/media", "./custom-templates:/templates"],
"volumes": ["./data:/data", "./custom-templates:/templates"],
},
"worker": {
"command": "worker",
@@ -62,7 +64,7 @@ base = {
"user": "root",
"volumes": [
"/var/run/docker.sock:/var/run/docker.sock",
"./media:/data/media",
"./data:/data",
"./certs:/certs",
"./custom-templates:/templates",
],

View File

@@ -249,36 +249,60 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
Raises a clear test failure if the element isn't found, the text doesn't appear
within `timeout` seconds, or the text is not valid JSON.
"""
use_body = context is None
wait_timeout = timeout or self.wait_timeout
def get_context() -> WebElement:
"""Get or refresh the context element."""
if use_body:
return self.driver.find_element(By.TAG_NAME, "body")
return context
def get_text_safely() -> str:
"""Get element text, re-finding element if stale."""
for _ in range(5):
try:
return get_context().text.strip()
except StaleElementReferenceException:
sleep(0.5)
return get_context().text.strip()
def get_inner_html_safely() -> str:
"""Get innerHTML, re-finding element if stale."""
for _ in range(5):
try:
return get_context().get_attribute("innerHTML") or ""
except StaleElementReferenceException:
sleep(0.5)
return get_context().get_attribute("innerHTML") or ""
try:
if context is None:
context = self.driver.find_element(By.TAG_NAME, "body")
get_context()
except NoSuchElementException:
self.fail(
f"No element found (defaulted to <body>). Current URL: {self.driver.current_url}"
)
wait_timeout = timeout or self.wait_timeout
wait = WebDriverWait(context, wait_timeout)
wait = WebDriverWait(self.driver, wait_timeout)
try:
wait.until(lambda d: len(d.text.strip()) != 0)
wait.until(lambda d: len(get_text_safely()) != 0)
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
snippet = get_text_safely()[:500].replace("\n", " ")
self.fail(
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
)
body_text = context.text.strip()
inner_html = context.get_attribute("innerHTML") or ""
body_text = get_text_safely()
inner_html = get_inner_html_safely()
if "redirecting" in inner_html.lower():
try:
wait.until(lambda d: "redirecting" not in d.get_attribute("innerHTML").lower())
wait.until(lambda d: "redirecting" not in get_inner_html_safely().lower())
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
inner_html = context.get_attribute("innerHTML") or ""
snippet = get_text_safely()[:500].replace("\n", " ")
inner_html = get_inner_html_safely()
self.fail(
f"Timed out waiting for redirect to finish at {self.driver.current_url}. "
@@ -286,8 +310,8 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
f"{inner_html or '<empty>'}"
)
inner_html = context.get_attribute("innerHTML") or ""
body_text = context.text.strip()
inner_html = get_inner_html_safely()
body_text = get_text_safely()
snippet = body_text[:500].replace("\n", " ")

1447
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -93,15 +93,15 @@
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/legacy-modes": "^6.5.2",
"@codemirror/theme-one-dark": "^6.1.3",
"@eslint/js": "^9.39.1",
"@eslint/js": "^9.39.2",
"@floating-ui/dom": "^1.7.4",
"@formatjs/intl-listformat": "^7.7.13",
"@fortawesome/fontawesome-free": "^7.1.0",
"@goauthentik/api": "^2025.10.0-rc1-1760614339",
"@goauthentik/core": "^1.0.0",
"@goauthentik/esbuild-plugin-live-reload": "^1.3.1",
"@goauthentik/eslint-config": "^1.1.1",
"@goauthentik/prettier-config": "^3.2.1",
"@goauthentik/esbuild-plugin-live-reload": "^1.4.0",
"@goauthentik/eslint-config": "^1.2.0",
"@goauthentik/prettier-config": "^3.3.1",
"@goauthentik/tsconfig": "^1.0.5",
"@hcaptcha/types": "^1.1.0",
"@lit/context": "^1.1.6",
@@ -117,20 +117,20 @@
"@patternfly/elements": "^4.2.0",
"@patternfly/patternfly": "^4.224.2",
"@playwright/test": "^1.57.0",
"@sentry/browser": "^10.29.0",
"@storybook/addon-docs": "^10.1.7",
"@storybook/addon-links": "^10.1.7",
"@storybook/web-components": "^10.1.7",
"@storybook/web-components-vite": "^10.1.7",
"@sentry/browser": "^10.31.0",
"@storybook/addon-docs": "^10.1.10",
"@storybook/addon-links": "^10.1.10",
"@storybook/web-components": "^10.1.10",
"@storybook/web-components-vite": "^10.1.10",
"@types/codemirror": "^5.60.17",
"@types/grecaptcha": "^3.0.9",
"@types/guacamole-common-js": "^1.5.5",
"@types/node": "^25.0.0",
"@types/node": "^25.0.3",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@typescript-eslint/eslint-plugin": "^8.47.0",
"@typescript-eslint/parser": "^8.47.0",
"@vitest/browser": "^4.0.15",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"@vitest/browser": "^4.0.16",
"@vitest/browser-playwright": "^4.0.15",
"@webcomponents/webcomponentsjs": "^2.8.0",
"base64-js": "^1.5.1",
@@ -143,15 +143,15 @@
"date-fns": "^4.1.0",
"deepmerge-ts": "^7.1.5",
"dompurify": "^3.3.1",
"esbuild": "^0.27.1",
"eslint": "^9.39.1",
"esbuild": "^0.27.2",
"eslint": "^9.39.2",
"eslint-plugin-lit": "^2.1.1",
"eslint-plugin-wc": "^3.0.2",
"fuse.js": "^7.1.0",
"globals": "^16.5.0",
"guacamole-common-js": "^1.5.0",
"hastscript": "^9.0.1",
"knip": "^5.73.1",
"knip": "^5.75.1",
"lex": "^2025.11.0",
"lit": "^3.3.1",
"lit-analyzer": "^2.0.3",
@@ -182,9 +182,9 @@
"turnstile-types": "^1.2.3",
"type-fest": "^5.3.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.49.0",
"typescript-eslint": "^8.50.0",
"unist-util-visit": "^5.0.0",
"vite": "^7.2.7",
"vite": "^7.3.0",
"vitest": "^4.0.15",
"webcomponent-qr-code": "^1.3.0",
"wireit": "^0.14.12",
@@ -194,10 +194,10 @@
"@esbuild/darwin-arm64": "^0.27.0",
"@esbuild/linux-arm64": "^0.27.0",
"@esbuild/linux-x64": "^0.27.0",
"@rollup/rollup-darwin-arm64": "^4.53.3",
"@rollup/rollup-linux-arm64-gnu": "^4.53.3",
"@rollup/rollup-linux-x64-gnu": "^4.53.3",
"chromedriver": "^143.0.1"
"@rollup/rollup-darwin-arm64": "^4.53.5",
"@rollup/rollup-linux-arm64-gnu": "^4.53.5",
"@rollup/rollup-linux-x64-gnu": "^4.53.5",
"chromedriver": "^143.0.2"
},
"wireit": {
"build": {

View File

@@ -44,7 +44,7 @@
},
"dependencies": {
"@goauthentik/tsconfig": "^1.0.5",
"@types/node": "^25.0.0",
"@types/node": "^25.0.3",
"@types/semver": "^7.7.1",
"semver": "^7.7.3",
"typescript": "^5.9.3"

View File

@@ -20,13 +20,13 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-swc": "^0.4.0",
"@swc/cli": "^0.7.9",
"@swc/core": "^1.15.3",
"@swc/core": "^1.15.6",
"base64-js": "^1.5.1",
"bootstrap": "^5.3.8",
"formdata-polyfill": "^2025.11.0",
"globby": "16.0.0",
"jquery": "^3.7.1",
"rollup": "^4.53.3",
"rollup": "^4.53.5",
"weakmap-polyfill": "^2.0.4"
},
"optionalDependencies": {

View File

@@ -67,24 +67,9 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
@property({ type: Boolean, attribute: "singleton" })
public singleton = false;
/**
* Set to `true` to include certificate details (fingerprints, expiry, certificate subject, key type)
* in the API response.
* Each returned certificate's PEM data must be parsed using cryptography library,
* public keys extracted, and hashes computed. With large result sets, this can add a lot of time
* to responses.
* Only enable when you actually need the detailed fields displayed in the UI.
* For simple certificate selection dropdowns, leave this as `false` (default).
* @attr
*/
@property({ type: Boolean, attribute: "include-details" })
public includeDetails = false;
/**
* When allowedKeyTypes is set, only certificates or keypairs with matching
* key algorithms will be shown. Since certificates must be parsed to
* extract algorithm details, an instance with many certificates may experience
* long delays and server performance slowdowns. Avoid setting this field whenever possible.
* key algorithms will be shown.
* @attr
* @example [KeyTypeEnum.Rsa, KeyTypeEnum.Ec]
*/
@@ -123,7 +108,6 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
const args: CryptoCertificatekeypairsListRequest = {
ordering: "name",
hasKey: !this.noKey,
includeDetails: this.includeDetails,
};
if (query !== undefined) {
args.search = query;

View File

@@ -45,9 +45,9 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList(
await this.defaultEndpointConfig(),
);
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
...(await this.defaultEndpointConfig()),
});
}
protected columns: TableColumn[] = [

View File

@@ -77,7 +77,11 @@ export class AgentConnectorSetup extends AKElement {
<p>${msg("Afterwards, select the enrollment token you want to use:")}</p>
</div>
<div class="pf-l-grid__item pf-m-12-col">
<p>${msg("Then download the configuration to deploy the authentik Agent")}</p>
<p>
${msg(
"Next, download the configuration to deploy the authentik Agent via MDM",
)}
</p>
</div>
</div>
<div class="pf-l-grid__item pf-m-6-col pf-l-grid">

View File

@@ -77,10 +77,13 @@ export class EnrollmentTokenForm extends WithBrandConfig(ModelForm<EnrollmentTok
value=${ifDefined(this.instance?.name)}
required
></ak-text-input>
<ak-form-element-horizontal label=${msg("Device Group")} name="deviceGroup">
<ak-form-element-horizontal label=${msg("Device Access Group")} name="deviceGroup">
<ak-endpoints-device-group-search
.group=${this.instance?.deviceGroup}
></ak-endpoints-device-group-search>
<p class="pf-c-form__helper-text">
${msg("Select a device access group to be added to upon enrollment.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="expiring">
<label class="pf-c-switch">

View File

@@ -68,7 +68,7 @@ export class DeviceViewPage extends AKElement {
? msg(str`Device ${this.device?.name}`)
: msg("Loading device..."),
description: this.device?.facts.data.os
? this.device?.facts.data.os?.name + " " + this.device?.facts.data.os?.version
? `${this.device?.facts.data.os?.name} ${this.device?.facts.data.os?.version}`
: undefined,
icon: "fa fa-laptop",
});
@@ -110,7 +110,7 @@ export class DeviceViewPage extends AKElement {
?good=${this.device.facts.data.network?.firewallEnabled}
></ak-status-label>`,
],
[msg("Group"), this.device.accessGroupObj?.name ?? "-"],
[msg("Device access group"), this.device.accessGroupObj?.name ?? "-"],
[
msg("Actions"),
html`<ak-forms-modal>
@@ -162,13 +162,13 @@ export class DeviceViewPage extends AKElement {
></ak-status-label>`,
],
[
msg("Disk size"),
msg("Primary disk size"),
rootDisk?.capacityTotalBytes
? getSize(rootDisk.capacityTotalBytes)
: "-",
],
[
msg("Disk usage"),
msg("Primary disk usage"),
rootDisk?.capacityTotalBytes && rootDisk.capacityUsedBytes
? html`<progress
value="${rootDisk.capacityUsedBytes}"

View File

@@ -91,6 +91,20 @@ export class DataExportListPage extends TablePage<DataExport> {
</div>
</dl>`;
}
protected renderEmpty(_inner?: TemplateResult): TemplateResult {
return super.renderEmpty(
html`<ak-empty-state icon=${this.pageIcon}
><span
>${msg(
html`To create a data export, navigate to
<a href="#/identity/users">Directory > Users</a> or to
<a href="#/events/log">Events > Logs</a>.`,
)}</span
>
</ak-empty-state>`,
);
}
}
declare global {

View File

@@ -16,18 +16,33 @@ import { createRef, ref } from "lit/directives/ref.js";
// Same regex is used in the backend as well
const VALID_FILE_NAME_PATTERN = /^[a-zA-Z0-9._/-]+$/;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
// This is perfect for the "pattern" attribute
const VALID_FILE_NAME_PATTERN_STRING = VALID_FILE_NAME_PATTERN.source;
// Note: browsers compile `pattern` using the new `v` RegExp flag (Unicode sets). Under `/v`,
// both `/` and `-` must be escaped inside character classes.
const VALID_FILE_NAME_PATTERN_STRING = "^[a-zA-Z0-9._\\/\\-]+$";
function assertValidFileName(fileName: string): void {
if (!VALID_FILE_NAME_PATTERN.test(fileName)) {
throw new Error(
msg("Filename can only contain letters, numbers, dots, hyphens, and underscores"),
msg(
"Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes",
),
);
}
}
function getFileExtension(fileName: string): string {
const lastDot = fileName.lastIndexOf(".");
if (lastDot <= 0) return "";
return fileName.slice(lastDot);
}
function hasBasenameExtension(fileName: string): boolean {
const baseName = fileName.split("/").pop() ?? fileName;
const lastDot = baseName.lastIndexOf(".");
return lastDot > 0;
}
@customElement("ak-file-upload-form")
export class FileUploadForm extends Form<Record<string, unknown>> {
@property({ type: String, useDefault: true })
@@ -57,36 +72,36 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
throw new PreventFormSubmit("Selected file not provided", this);
}
assertValidFileName(this.selectedFile.name);
const api = new AdminApi(DEFAULT_CONFIG);
const customName = typeof data.fileName === "string" ? data.fileName.trim() : "";
const customName = typeof data.name === "string" ? data.name.trim() : "";
// If custom name provided, validate and append original extension
// Only validate the original filename if no custom name is provided
let finalName = this.selectedFile.name;
if (customName) {
assertValidFileName(customName);
const ext = this.selectedFile.name.substring(this.selectedFile.name.lastIndexOf("."));
finalName = customName + ext;
const ext = getFileExtension(this.selectedFile.name);
finalName =
ext && !hasBasenameExtension(customName) ? `${customName}${ext}` : customName;
} else {
assertValidFileName(this.selectedFile.name);
}
return api
.adminFileCreate({
file: this.selectedFile,
name: finalName,
usage: this.usage,
})
.then(() => {
showMessage({
level: MessageLevel.success,
message: msg("File uploaded successfully"),
});
assertValidFileName(finalName);
this.reset();
})
.finally(() => {
this.clearFileInput();
});
await api.adminFileCreate({
file: this.selectedFile,
name: finalName,
usage: this.usage,
});
showMessage({
level: MessageLevel.success,
message: msg("File uploaded successfully"),
});
this.reset();
this.clearFileInput();
}
renderForm() {
@@ -101,7 +116,7 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
@change=${this.#fileChangeListener}
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("File Name")} name="fileName">
<ak-form-element-horizontal label=${msg("File Name")} name="name">
<input
type="text"
class="pf-c-form-control"

View File

@@ -71,40 +71,45 @@ export class RoleObjectPermissionForm extends ModelForm<RoleAssignData, number>
if (!this.modelPermissions) {
return nothing;
}
return html`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal label=${msg("Role")} name="role">
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Role[]> => {
const args: RbacRolesListRequest = {
ordering: "name",
};
if (query !== undefined) {
args.search = query;
}
const roles = await new RbacApi(DEFAULT_CONFIG).rbacRolesList(args);
return roles.results;
}}
.renderElement=${(role: Role): string => {
return role.name;
}}
.value=${(role: Role | undefined): string | undefined => {
return role?.pk;
}}
>
</ak-search-select>
</ak-form-element-horizontal>
${this.modelPermissions?.results
.filter((perm) => {
const [_app, model] = this.model?.split(".") || "";
return perm.codename !== `add_${model}`;
})
.map((perm) => {
return html`<ak-switch-input
name="permissions.${perm.codename}"
label=${perm.name}
></ak-switch-input>`;
})}
</form>`;
return html`<span
>${msg(
"Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.",
)}</span
>
<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal label=${msg("Role")} name="role">
<ak-search-select
.fetchObjects=${async (query?: string): Promise<Role[]> => {
const args: RbacRolesListRequest = {
ordering: "name",
};
if (query !== undefined) {
args.search = query;
}
const roles = await new RbacApi(DEFAULT_CONFIG).rbacRolesList(args);
return roles.results;
}}
.renderElement=${(role: Role): string => {
return role.name;
}}
.value=${(role: Role | undefined): string | undefined => {
return role?.pk;
}}
>
</ak-search-select>
</ak-form-element-horizontal>
${this.modelPermissions?.results
.filter((perm) => {
const [_app, model] = this.model?.split(".") || "";
return perm.codename !== `add_${model}`;
})
.map((perm) => {
return html`<ak-switch-input
name="permissions.${perm.codename}"
label=${perm.name}
></ak-switch-input>`;
})}
</form>`;
}
}

View File

@@ -87,7 +87,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
renderObjectCreate(): TemplateResult {
return html`<ak-forms-modal>
<span slot="submit">${msg("Assign")}</span>
<span slot="header">${msg("Assign permission to role")}</span>
<span slot="header">${msg("Assign object permissions to role")}</span>
<ak-rbac-role-object-permission-form
model=${ifDefined(this.model)}
objectPk=${ifDefined(this.objectPk)}
@@ -95,7 +95,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
>
</ak-rbac-role-object-permission-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Assign role permissions")}
${msg("Assign Object Permission")}
</button>
</ak-forms-modal>`;
}
@@ -135,9 +135,9 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
const assignedToModel = item.modelPermissions.some(
(uperm) => uperm.codename === perm.codename,
);
const assignedToObject = item.objectPermissions.some(
(uperm) => uperm.codename === perm.codename,
);
const assignedToObject = item.objectPermissions
.filter((uperm) => uperm.objectPk === this.objectPk)
.some((uperm) => uperm.codename === perm.codename);
let tooltip: string | null = null;
if (assignedToModel && assignedToObject) {

View File

@@ -51,6 +51,11 @@ export class NavigationButtons extends WithSession(AKElement) {
Styles,
];
connectedCallback(): void {
super.connectedCallback();
this.refreshNotifications();
}
protected async refreshNotifications(): Promise<void> {
const { currentUser } = this;

View File

@@ -4183,9 +4183,6 @@ neprojde, když jedna nebo obě z vybraných možností jsou rovny nebo nad prah
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4284,12 +4281,6 @@ neprojde, když jedna nebo obě z vybraných možností jsou rovny nebo nad prah
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9550,9 +9541,6 @@ Vazby na skupiny/uživatele jsou kontrolovány vůči uživateli události.</tar
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9685,9 +9673,6 @@ Vazby na skupiny/uživatele jsou kontrolovány vůči uživateli události.</tar
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9901,6 +9886,41 @@ Vazby na skupiny/uživatele jsou kontrolovány vůči uživateli události.</tar
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4214,9 +4214,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4315,12 +4312,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9590,9 +9581,6 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9725,9 +9713,6 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9941,6 +9926,41 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3220,9 +3220,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -3319,12 +3316,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -7383,9 +7374,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -7518,9 +7506,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -7734,6 +7719,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4151,9 +4151,6 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4252,12 +4249,6 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9510,9 +9501,6 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9645,9 +9633,6 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9861,6 +9846,41 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4293,9 +4293,6 @@ läpäisy estyy kun jompi kumpi tai molemmat vaihtoehdot ylittävät raja-arvon.
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4394,12 +4391,6 @@ läpäisy estyy kun jompi kumpi tai molemmat vaihtoehdot ylittävät raja-arvon.
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9773,9 +9764,6 @@ Liitokset käyttäjiin/ryhmiin tarkistetaan tapahtuman käyttäjästä.</target>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9908,9 +9896,6 @@ Liitokset käyttäjiin/ryhmiin tarkistetaan tapahtuman käyttäjästä.</target>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -10124,6 +10109,41 @@ Liitokset käyttäjiin/ryhmiin tarkistetaan tapahtuman käyttäjästä.</target>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4282,9 +4282,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4383,12 +4380,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9758,9 +9749,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9893,9 +9881,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -10109,6 +10094,41 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4110,9 +4110,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4211,12 +4208,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9458,9 +9449,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9593,9 +9581,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9809,6 +9794,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4284,9 +4284,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4385,12 +4382,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9752,9 +9743,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9887,9 +9875,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -10103,6 +10088,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3951,9 +3951,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4052,12 +4049,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9083,9 +9074,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9218,9 +9206,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9434,6 +9419,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3789,9 +3789,6 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -3890,12 +3887,6 @@ slaagt niet wanneer een of beide geselecteerde opties gelijk zijn aan of boven d
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -8731,9 +8722,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -8866,9 +8854,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9082,6 +9067,41 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3966,9 +3966,6 @@ nie przechodzi, gdy jedna lub obie wybrane opcje są równe lub wyższe od progu
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4067,12 +4064,6 @@ nie przechodzi, gdy jedna lub obie wybrane opcje są równe lub wyższe od progu
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9113,9 +9104,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9248,9 +9236,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9464,6 +9449,41 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4287,9 +4287,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4388,12 +4385,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9741,9 +9732,6 @@ por exemplo: <x id="0" equiv-text="&lt;code&gt;"/>oci://registry.domain.tld/path
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9876,9 +9864,6 @@ por exemplo: <x id="0" equiv-text="&lt;code&gt;"/>oci://registry.domain.tld/path
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -10092,6 +10077,41 @@ por exemplo: <x id="0" equiv-text="&lt;code&gt;"/>oci://registry.domain.tld/path
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4004,9 +4004,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4105,12 +4102,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9201,9 +9192,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9336,9 +9324,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9552,6 +9537,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3983,9 +3983,6 @@ Belirlenen seçeneklerden biri veya her ikisi de eşiğe eşit veya eşiğin üz
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4084,12 +4081,6 @@ Belirlenen seçeneklerden biri veya her ikisi de eşiğe eşit veya eşiğin üz
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9179,9 +9170,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9314,9 +9302,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9530,6 +9515,41 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -4250,9 +4250,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -4351,12 +4348,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -9713,9 +9704,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -9848,9 +9836,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -10064,6 +10049,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@@ -3821,9 +3821,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="sf96a86df0756bc7b">
<source>Afterwards, select the enrollment token you want to use:</source>
</trans-unit>
<trans-unit id="s13b4cf044a01dde2">
<source>Then download the configuration to deploy the authentik Agent</source>
</trans-unit>
<trans-unit id="s12f523a52b843ea2">
<source>macOS</source>
</trans-unit>
@@ -3922,12 +3919,6 @@ doesn't pass when either or both of the selected options are equal or above the
<trans-unit id="s94a50f1495fb5ccd">
<source>Disk encryption</source>
</trans-unit>
<trans-unit id="s76ea179414f2b2a5">
<source>Disk size</source>
</trans-unit>
<trans-unit id="s25e7a078391a3ec3">
<source>Disk usage</source>
</trans-unit>
<trans-unit id="s416211a967a6db4e">
<source>Users / Groups</source>
</trans-unit>
@@ -8794,9 +8785,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s6df8326edea3b23d">
<source>If no device was provided, this stage will stop flow execution.</source>
</trans-unit>
<trans-unit id="s506c7d2e87f6770e">
<source>Filename can only contain letters, numbers, dots, hyphens, and underscores</source>
</trans-unit>
<trans-unit id="s3a3d5b2575cd32ea">
<source>File uploaded successfully</source>
</trans-unit>
@@ -8929,9 +8917,6 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc47f8ab6162bb2bb">
<source>Outpost configuration</source>
</trans-unit>
<trans-unit id="s184c3b30bebb2dd8">
<source>Assign role permissions</source>
</trans-unit>
<trans-unit id="s11a2ac9f2bd811d8">
<source>Delete Object Permission</source>
</trans-unit>
@@ -9145,6 +9130,41 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="s46a03121a2c260ea">
<source>Buffer PolicyAccessView requests</source>
</trans-unit>
<trans-unit id="s361e9d929ee925e6">
<source>Assign Object Permission</source>
</trans-unit>
<trans-unit id="s5f6ad947b4824e40">
<source>Next, download the configuration to deploy the authentik Agent via MDM</source>
</trans-unit>
<trans-unit id="s523337424b694d5c">
<source>Device Access Group</source>
</trans-unit>
<trans-unit id="s7642bf28cf8f476c">
<source>Select a device access group to be added to upon enrollment.</source>
</trans-unit>
<trans-unit id="h30bbf18fbf87fa57">
<source>To create a data export, navigate to
<x id="0" equiv-text="&lt;a href=&quot;#/identity/users&quot;&gt;"/>Directory &gt; Users<x id="1" equiv-text="&lt;/a&gt;"/> or to
<x id="2" equiv-text="&lt;a href=&quot;#/events/log&quot;&gt;"/>Events &gt; Logs<x id="3" equiv-text="&lt;/a&gt;"/>.</source>
</trans-unit>
<trans-unit id="s813faec5ff1d32d1">
<source>Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes</source>
</trans-unit>
<trans-unit id="s54af3ec70642782c">
<source>Choose the object permissions that you want the selected role to have on this object. These object permissions are in addition to any global permissions already within the role.</source>
</trans-unit>
<trans-unit id="sb3be65525dd1f92c">
<source>Assign object permissions to role</source>
</trans-unit>
<trans-unit id="sbcd108b66363075c">
<source>Device access group</source>
</trans-unit>
<trans-unit id="s325acae04cdcac57">
<source>Primary disk size</source>
</trans-unit>
<trans-unit id="sb4a957846c89fca1">
<source>Primary disk usage</source>
</trans-unit>
</body>
</file>
</xliff>

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