Compare commits

..

274 Commits

Author SHA1 Message Date
5b9bb12822 web: confirmation prompt updated to the latest version 2024-06-13 09:43:05 -07:00
8f09955d58 Merge branch 'main' into 5165-password-strength-indicator
* main: (2701 commits)
  website/developer-docs: add a baby Style Guide (#9900)
  website/integrations: gitlab: update certificate key pair location and specify sha (#9925)
  root: handle asgi exception (#10085)
  website: bump prettier from 3.3.1 to 3.3.2 in /website (#10082)
  web: bump prettier from 3.3.1 to 3.3.2 in /web (#10081)
  core: bump google-api-python-client from 2.132.0 to 2.133.0 (#10083)
  web: bump prettier from 3.3.1 to 3.3.2 in /tests/wdio (#10079)
  web: bump chromedriver from 125.0.3 to 126.0.0 in /tests/wdio (#10078)
  web: bump @sentry/browser from 8.8.0 to 8.9.1 in /web in the sentry group (#10080)
  web: bump braces from 3.0.2 to 3.0.3 in /web (#10077)
  website: bump braces from 3.0.2 to 3.0.3 in /website (#10076)
  web: bump braces from 3.0.2 to 3.0.3 in /tests/wdio (#10075)
  core: bump azure-identity from 1.16.0 to 1.16.1 (#10071)
  rbac: filters: fix missing attribute for unauthenticated requests (#10061)
  tests/e2e: docker-compose.yml: remove version element forgotten last time (#10067)
  providers/microsoft_entra: fix error when updating connection attributes (#10039)
  website/integrations: aws: fix about service link (#10062)
  translate: Updates for file locale/en/LC_MESSAGES/django.po in it (#10060)
  core: bump github.com/redis/go-redis/v9 from 9.5.2 to 9.5.3 (#10046)
  core: bump github.com/gorilla/websocket from 1.5.1 to 1.5.2 (#10047)
  ...
2024-06-13 08:17:33 -07:00
8f82dac84e website/developer-docs: add a baby Style Guide (#9900)
* draft style guide

* broken links

* rename partial file

* added content to style guide, more links

* sidebar and link

* polish

* typo

* forgot to save

* kens formatting trick

* typo

* Jens' edits

* tweak

* simplify formatting for variables

* edits from 4d62

* fixed link

* more edits

* tweak re formatting command

* tweak re formatting command

---------

Co-authored-by: Tana M Berry <tana@goauthentik.com>
2024-06-13 09:10:59 -05:00
89c08f2588 website/integrations: gitlab: update certificate key pair location and specify sha (#9925)
* website/integrations: gitlab: update certificate key pair location and specify sha

Signed-off-by: 4d62 <ext@4d62.me>

* i prefer the previous wording

Signed-off-by: 4d62 <ext@4d62.me>

* suggest

Co-authored-by: Jens L. <jens@beryju.org>
Signed-off-by: 4d62 <ext@4d62.me>

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

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* p

---------

Signed-off-by: 4d62 <ext@4d62.me>
Co-authored-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-06-12 16:37:45 -05:00
113d6cc45f root: handle asgi exception (#10085) 2024-06-12 16:56:18 +09:00
05cfbca5f2 website: bump prettier from 3.3.1 to 3.3.2 in /website (#10082)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.1...3.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 15:40:28 +09:00
385f9bcbac web: bump prettier from 3.3.1 to 3.3.2 in /web (#10081)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.1...3.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 15:40:07 +09:00
86bc5b4cdb core: bump google-api-python-client from 2.132.0 to 2.133.0 (#10083)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.132.0 to 2.133.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.132.0...v2.133.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 15:39:44 +09:00
8d5d9f35ef web: bump prettier from 3.3.1 to 3.3.2 in /tests/wdio (#10079)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.1...3.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 13:33:25 +09:00
439169d5e7 web: bump chromedriver from 125.0.3 to 126.0.0 in /tests/wdio (#10078)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 125.0.3 to 126.0.0.
- [Commits](https://github.com/giggio/node-chromedriver/compare/125.0.3...126.0.0)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 13:33:15 +09:00
e405b33f9f web: bump @sentry/browser from 8.8.0 to 8.9.1 in /web in the sentry group (#10080)
web: bump @sentry/browser in /web in the sentry group

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


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

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-06-12 13:33:00 +09:00
a9c13d4d10 web: bump braces from 3.0.2 to 3.0.3 in /web (#10077)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 13:30:03 +09:00
12b16b17a2 website: bump braces from 3.0.2 to 3.0.3 in /website (#10076)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 13:29:46 +09:00
3473abee32 web: bump braces from 3.0.2 to 3.0.3 in /tests/wdio (#10075)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 13:29:35 +09:00
b5a0b3a521 core: bump azure-identity from 1.16.0 to 1.16.1 (#10071)
Bumps [azure-identity](https://github.com/Azure/azure-sdk-for-python) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/Azure/azure-sdk-for-python/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/esrp_release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-python/compare/azure-identity_1.16.0...azure-identity_1.16.1)

---
updated-dependencies:
- dependency-name: azure-identity
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 12:54:59 +09:00
837a0325ca rbac: filters: fix missing attribute for unauthenticated requests (#10061) 2024-06-11 14:36:09 +00:00
b1050e8825 tests/e2e: docker-compose.yml: remove version element forgotten last time (#10067) 2024-06-11 16:16:24 +02:00
7bb90b1661 providers/microsoft_entra: fix error when updating connection attributes (#10039)
* providers/microsoft_entra: fix error when updating connection attributes

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

* include URL to field references

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

* only set gws user password when creating by default

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

* merge instead of replace connection attributes

an update might not return all attributes so we don't want to fully replace the attributes

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-11 16:03:23 +09:00
8f755785ea website/integrations: aws: fix about service link (#10062)
Signed-off-by: 4d62 <ext@4d62.me>
2024-06-11 14:33:57 +09:00
adc0ec8ee4 translate: Updates for file locale/en/LC_MESSAGES/django.po in it (#10060)
Translate locale/en/LC_MESSAGES/django.po in it

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-10 13:31:22 +00:00
9379b3d178 core: bump github.com/redis/go-redis/v9 from 9.5.2 to 9.5.3 (#10046)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.5.2 to 9.5.3.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.5.2...v9.5.3)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:32:11 +09:00
18eef8ae05 core: bump github.com/gorilla/websocket from 1.5.1 to 1.5.2 (#10047)
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:31:55 +09:00
f0e22fd08b translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#10058)
Translate locale/en/LC_MESSAGES/django.po in ru

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-10 20:31:18 +09:00
ce5297a6cd core: bump sentry-sdk from 2.5.0 to 2.5.1 (#10048)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.5.0...2.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:30:54 +09:00
74c8df8782 core: bump django-cte from 1.3.2 to 1.3.3 (#10049)
Bumps [django-cte](https://github.com/dimagi/django-cte) from 1.3.2 to 1.3.3.
- [Changelog](https://github.com/dimagi/django-cte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dimagi/django-cte/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:30:36 +09:00
2d897fac48 core: bump packaging from 24.0 to 24.1 (#10050)
Bumps [packaging](https://github.com/pypa/packaging) from 24.0 to 24.1.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/24.0...24.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:25:14 +09:00
2eb47c9efa web: bump yaml from 2.4.3 to 2.4.5 in /web (#10054)
Bumps [yaml](https://github.com/eemeli/yaml) from 2.4.3 to 2.4.5.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.4.3...v2.4.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:24:59 +09:00
9852041f38 web: bump @sentry/browser from 8.7.0 to 8.8.0 in /web in the sentry group (#10051)
web: bump @sentry/browser in /web in the sentry group

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


Updates `@sentry/browser` from 8.7.0 to 8.8.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/8.7.0...8.8.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-06-10 20:24:28 +09:00
7f74936212 web: bump esbuild from 0.21.4 to 0.21.5 in /web (#10053)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.21.4 to 0.21.5.
- [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.21.4...v0.21.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 20:20:53 +09:00
4f40b1e27c crypto: update fingerprint at same time as certificate (#10036)
Previously the fingerprint was only set when initially adding a key, if it
changed for any reason (like a renewed certificate) then every execution of
`Get` would lead to a full update. The certificate itself got cached, but the
fingerprint remained stale for next time.

This increased the chance of a fatal race during the cache update.

closes #9907
2024-06-10 20:16:35 +09:00
041e407153 translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#10056)
* Translate locale/en/LC_MESSAGES/django.po in ru

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

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

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-10 12:24:02 +02:00
31b891428e website/integrations: gitea: fix helm values (#10043) 2024-06-10 09:29:17 +00:00
4ab8247847 core, web: update translations (#10038)
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-10 11:23:00 +02:00
af24edf8c1 translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#10040)
* Translate locale/en/LC_MESSAGES/django.po in ru

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

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

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-10 11:22:51 +02:00
f8bfd12e31 docs/troubleshooting: upgrade docker: erroneous command (#10044) 2024-06-10 11:06:41 +02:00
993fc4b77b core: bump tornado from 6.4 to 6.4.1 (#10014)
Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 21:45:10 +09:00
3ee2be09bf core: bump twilio from 9.1.0 to 9.1.1 (#10016)
Bumps [twilio](https://github.com/twilio/twilio-python) from 9.1.0 to 9.1.1.
- [Release notes](https://github.com/twilio/twilio-python/releases)
- [Changelog](https://github.com/twilio/twilio-python/blob/main/CHANGES.md)
- [Commits](https://github.com/twilio/twilio-python/compare/9.1.0...9.1.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 21:44:42 +09:00
b3e3948f44 core: bump sentry-sdk from 2.4.0 to 2.5.0 (#10017)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.4.0 to 2.5.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.4.0...2.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 21:44:31 +09:00
117a5cd88d core: bump kubernetes from 29.0.0 to 30.1.0 (#10018)
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 29.0.0 to 30.1.0.
- [Release notes](https://github.com/kubernetes-client/python/releases)
- [Changelog](https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes-client/python/compare/v29.0.0...v30.1.0)

---
updated-dependencies:
- dependency-name: kubernetes
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 21:44:05 +09:00
342a40212e translate: Updates for file locale/en/LC_MESSAGES/django.po in pl (#10029)
Translate locale/en/LC_MESSAGES/django.po in pl

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-08 21:37:46 +09:00
15ae11d9d5 translate: Updates for file web/xliff/en.xlf in pl (#10030)
Translate web/xliff/en.xlf in pl

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-08 21:37:30 +09:00
e11df56f21 core, web: update translations (#10031)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-08 21:37:06 +09:00
3771af5282 translate: Updates for file web/xliff/en.xlf in zh_CN (#10032)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-08 21:36:53 +09:00
0718053c56 translate: Updates for file web/xliff/en.xlf in zh-Hans (#10033)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-08 21:36:40 +09:00
3e5014bfea web/admin: fix scim provider user list (#10028) 2024-06-07 19:48:11 +00:00
78f49ddc04 translate: Updates for file web/xliff/en.xlf in fr (#10024)
Translate web/xliff/en.xlf in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-07 18:20:51 +02:00
744bc54231 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#10025)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-06-07 18:20:20 +02:00
44ba30ad75 translate: Updates for file locale/en/LC_MESSAGES/django.po in fr (#10022)
Translate locale/en/LC_MESSAGES/django.po in fr

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-07 14:52:38 +02:00
63b991f137 core, web: update translations (#10015)
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-07 13:15:03 +02:00
7f9acb8a2b translate: Updates for file locale/en/LC_MESSAGES/django.po in pl (#10019)
* Translate locale/en/LC_MESSAGES/django.po in pl

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

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

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-07 13:14:44 +02:00
ce3ba32044 translate: Updates for file locale/en/LC_MESSAGES/django.po in pl (#10011)
Translate locale/en/LC_MESSAGES/django.po in pl

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-06 20:49:42 +09:00
25e6a69331 providers/sync: update attributes on update (#10012)
* unrelated

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

* providers/sync: update attributes in connection updates after updating remote object

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-06 20:49:33 +09:00
78d07cc355 website: bump prettier from 3.3.0 to 3.3.1 in /website (#10005)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.0...3.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 14:17:22 +09:00
3c4df47fe3 web: bump the babel group in /web with 7 updates (#10006)
Bumps the babel group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.24.6` | `7.24.7` |
| [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) | `7.24.6` | `7.24.7` |
| [@babel/plugin-transform-private-methods](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-private-methods) | `7.24.6` | `7.24.7` |
| [@babel/plugin-transform-private-property-in-object](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-private-property-in-object) | `7.24.6` | `7.24.7` |
| [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) | `7.24.6` | `7.24.7` |
| [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.24.6` | `7.24.7` |
| [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) | `7.24.6` | `7.24.7` |


Updates `@babel/core` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-core)

Updates `@babel/plugin-proposal-decorators` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-plugin-proposal-decorators)

Updates `@babel/plugin-transform-private-methods` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-plugin-transform-private-methods)

Updates `@babel/plugin-transform-private-property-in-object` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-plugin-transform-private-property-in-object)

Updates `@babel/plugin-transform-runtime` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-plugin-transform-runtime)

Updates `@babel/preset-env` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-preset-env)

Updates `@babel/preset-typescript` from 7.24.6 to 7.24.7
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-proposal-decorators"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-private-methods"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-private-property-in-object"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 14:17:13 +09:00
8ed1805cb8 web: bump prettier from 3.3.0 to 3.3.1 in /tests/wdio (#10003)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.0...3.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 14:17:02 +09:00
4d23db73ca web: bump the storybook group in /web with 7 updates (#10007)
Bumps the storybook group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.1.5` | `8.1.6` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.1.5` | `8.1.6` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) | `8.1.5` | `8.1.6` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.1.5` | `8.1.6` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `8.1.5` | `8.1.6` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `8.1.5` | `8.1.6` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.1.5` | `8.1.6` |


Updates `@storybook/addon-essentials` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/addons/essentials)

Updates `@storybook/addon-links` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/addons/links)

Updates `@storybook/blocks` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/ui/blocks)

Updates `@storybook/manager-api` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/lib/manager-api)

Updates `@storybook/web-components` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/frameworks/web-components-vite)

Updates `storybook` from 8.1.5 to 8.1.6
- [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/v8.1.6/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  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>
2024-06-06 14:16:52 +09:00
72783953fb web: bump @rollup/plugin-replace from 5.0.6 to 5.0.7 in /web in the rollup group (#10008)
web: bump @rollup/plugin-replace in /web in the rollup group

Bumps the rollup group in /web with 1 update: [@rollup/plugin-replace](https://github.com/rollup/plugins/tree/HEAD/packages/replace).


Updates `@rollup/plugin-replace` from 5.0.6 to 5.0.7
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/replace/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/replace-v5.0.7/packages/replace)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-replace"
  dependency-type: direct:development
  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>
2024-06-06 14:16:42 +09:00
3b0fdb3dbd web: bump prettier from 3.3.0 to 3.3.1 in /web (#10009)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.3.0...3.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 14:16:32 +09:00
23161eed12 core: bump goauthentik.io/api/v3 from 3.2024042.10 to 3.2024042.11 (#10001)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.10 to 3.2024042.11.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.10...v3.2024042.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 13:22:19 +09:00
8918427588 core: bump ruff from 0.4.7 to 0.4.8 (#10002)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.7 to 0.4.8.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.7...v0.4.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 13:22:05 +09:00
5d858020f6 stages/email: fix sanitization of email addresses (#9999)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-06 13:20:26 +09:00
198e8b98a8 web: bump API Client version (#10000)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-06-06 04:10:13 +00:00
88e9c9b669 providers/sync: improve v3 (#9966)
* make external id field externally visible

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

* catch up scim provider

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

* add missing views to scim provider

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

* make neither user nor group required for mapping testing

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

* improve SkipObject handling

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

* allow deletion of connection objects

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

* make entra logs less noisy

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

* make event_matcher less noisy

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-06 12:47:48 +09:00
0c652a210d core: groups api: always prefetch users (#9974)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-06-06 12:47:33 +09:00
105a90d2e7 core, web: update translations (#9998)
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-06 00:30:01 +00:00
68f5abe318 translate: Updates for file web/xliff/en.xlf in zh_CN (#9992)
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-05 13:06:30 +02:00
59d4c18636 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9991)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-05 13:06:16 +02:00
b67e2a1144 translate: Updates for file web/xliff/en.xlf in zh-Hans (#9993)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-05 13:06:09 +02:00
fc025651ce translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9994)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-05 13:06:01 +02:00
7cedc840b0 core: bump golang.org/x/oauth2 from 0.20.0 to 0.21.0 (#9986)
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.20.0 to 0.21.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.20.0...v0.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:13:18 +09:00
5ba731e48b core, web: update translations (#9979)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-05 14:09:09 +09:00
c792534a50 web: bump @lit/context from 1.1.1 to 1.1.2 in /web (#9981)
Bumps [@lit/context](https://github.com/lit/lit/tree/HEAD/packages/context) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/context/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/@lit/context@1.1.2/packages/context)

---
updated-dependencies:
- dependency-name: "@lit/context"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:08:17 +09:00
a136fd8b54 core: bump sentry-sdk from 2.3.1 to 2.4.0 (#9985)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.3.1 to 2.4.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.3.1...2.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:07:41 +09:00
fb63c1f3e9 core: bump goauthentik.io/api/v3 from 3.2024042.9 to 3.2024042.10 (#9987)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.9 to 3.2024042.10.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.9...v3.2024042.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:07:09 +09:00
0b15ab3f27 core: bump google-api-python-client from 2.131.0 to 2.132.0 (#9988)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.131.0 to 2.132.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.131.0...v2.132.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:06:58 +09:00
f9fd67c2b8 core: bump pytest from 8.2.1 to 8.2.2 (#9989)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.2.1 to 8.2.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.2.1...8.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:06:26 +09:00
4ac19f9d44 core: bump cryptography from 42.0.7 to 42.0.8 (#9990)
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.7 to 42.0.8.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.7...42.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:05:17 +09:00
74d3e92bac web: bump tslib from 2.6.2 to 2.6.3 in /web (#9984)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.6.2 to 2.6.3.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/v2.6.2...v2.6.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 14:04:49 +09:00
207fa13405 web: bump country-flag-icons from 1.5.11 to 1.5.12 in /web (#9983)
Bumps [country-flag-icons](https://gitlab.com/catamphetamine/country-flag-icons) from 1.5.11 to 1.5.12.
- [Changelog](https://gitlab.com/catamphetamine/country-flag-icons/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/country-flag-icons/compare/v1.5.11...v1.5.12)

---
updated-dependencies:
- dependency-name: country-flag-icons
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 13:32:49 +09:00
208e4a8bed web: bump @rollup/plugin-replace from 5.0.5 to 5.0.6 in /web in the rollup group (#9980)
web: bump @rollup/plugin-replace in /web in the rollup group

Bumps the rollup group in /web with 1 update: [@rollup/plugin-replace](https://github.com/rollup/plugins/tree/HEAD/packages/replace).


Updates `@rollup/plugin-replace` from 5.0.5 to 5.0.6
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/replace/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/replace-v5.0.6/packages/replace)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-replace"
  dependency-type: direct:development
  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>
2024-06-05 13:32:31 +09:00
ee245ab390 web: bump lit from 3.1.3 to 3.1.4 in /web (#9982)
Bumps [lit](https://github.com/lit/lit/tree/HEAD/packages/lit) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/lit/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/lit@3.1.4/packages/lit)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-05 13:32:15 +09:00
60c8837082 web: bump API Client version (#9967)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-06-04 18:31:21 +09:00
6cf418a37e stages/captcha: rework (#9959)
* stages/captcha: rework

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

* update form with continue option

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

* update docs, add notes for scores

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

* also only raise error from success if needed

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

* return full data

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-04 18:25:38 +09:00
254761e930 website/docs: fixed broken links to helm repo (#9960)
fixed broken links to helm repo

the links to the helm repos were broken, the changes in b93bc6bb8e changed the name of the files.

Signed-off-by: Kenneth Foster <42386206+kefoster951@users.noreply.github.com>
2024-06-04 18:04:15 +09:00
d85f8758fe core: bump pydantic from 2.7.2 to 2.7.3 (#9962)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.7.2 to 2.7.3.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.7.2...v2.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 15:50:17 +09:00
84bfb37b60 web: bump the wdio group in /tests/wdio with 4 updates (#9963)
Bumps the wdio group in /tests/wdio with 4 updates: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli), [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner), [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/cli` from 8.38.1 to 8.38.2
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.2/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.2/packages/wdio-cli)

Updates `@wdio/local-runner` from 8.38.0 to 8.38.2
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.2/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.2/packages/wdio-local-runner)

Updates `@wdio/mocha-framework` from 8.38.0 to 8.38.2
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.2/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.2/packages/wdio-mocha-framework)

Updates `@wdio/spec-reporter` from 8.38.0 to 8.38.2
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.2/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.2/packages/wdio-spec-reporter)

---
updated-dependencies:
- dependency-name: "@wdio/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/local-runner"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/mocha-framework"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
- dependency-name: "@wdio/spec-reporter"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-04 15:50:00 +09:00
578ff13868 translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#9943)
Translate locale/en/LC_MESSAGES/django.po in ru

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-03 23:37:20 +09:00
d12acb5bcd translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9944)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-03 23:37:06 +09:00
0e8b9a6409 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9942)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-06-03 23:36:52 +09:00
6171443e61 stages/authenticator_webauthn: migrate device type import to systemtask and schedule (#9958)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-06-03 23:36:27 +09:00
5fedd616d9 website: bump prettier from 3.2.5 to 3.3.0 in /website (#9951)
* website: bump prettier from 3.2.5 to 3.3.0 in /website

Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.0)

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

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

* format

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-06-03 23:35:07 +09:00
5dd6498694 web: bump yaml from 2.4.2 to 2.4.3 in /web (#9945)
Bumps [yaml](https://github.com/eemeli/yaml) from 2.4.2 to 2.4.3.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.4.2...v2.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:13:13 +09:00
cf5102ed20 web: bump prettier from 3.2.5 to 3.3.0 in /web (#9946)
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:13:03 +09:00
d3b2032c33 core: bump uvicorn from 0.30.0 to 0.30.1 (#9948)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.30.0 to 0.30.1.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.30.0...0.30.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:12:47 +09:00
1e5df1c405 core: bump ruff from 0.4.6 to 0.4.7 (#9949)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.6 to 0.4.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.6...v0.4.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:12:34 +09:00
96eabe269c website: bump the docusaurus group in /website with 9 updates (#9950)
Bumps the docusaurus group in /website with 9 updates:

| Package | From | To |
| --- | --- | --- |
| [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.3.2` | `3.4.0` |
| [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) | `3.3.2` | `3.4.0` |
| [@docusaurus/plugin-content-docs](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-content-docs) | `3.3.2` | `3.4.0` |
| [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.3.2` | `3.4.0` |
| [@docusaurus/theme-common](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-theme-common) | `3.3.2` | `3.4.0` |
| [@docusaurus/theme-mermaid](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-theme-mermaid) | `3.3.2` | `3.4.0` |
| [@docusaurus/module-type-aliases](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-module-type-aliases) | `3.3.2` | `3.4.0` |
| [@docusaurus/tsconfig](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-tsconfig) | `3.3.2` | `3.4.0` |
| [@docusaurus/types](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-types) | `3.3.2` | `3.4.0` |


Updates `@docusaurus/core` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus)

Updates `@docusaurus/plugin-client-redirects` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-plugin-client-redirects)

Updates `@docusaurus/plugin-content-docs` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-plugin-content-docs)

Updates `@docusaurus/preset-classic` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-preset-classic)

Updates `@docusaurus/theme-common` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-theme-common)

Updates `@docusaurus/theme-mermaid` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-theme-mermaid)

Updates `@docusaurus/module-type-aliases` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-module-type-aliases)

Updates `@docusaurus/tsconfig` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-tsconfig)

Updates `@docusaurus/types` from 3.3.2 to 3.4.0
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v3.4.0/packages/docusaurus-types)

---
updated-dependencies:
- dependency-name: "@docusaurus/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/plugin-client-redirects"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/plugin-content-docs"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/preset-classic"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/theme-common"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/theme-mermaid"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/module-type-aliases"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/tsconfig"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docusaurus
- dependency-name: "@docusaurus/types"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: docusaurus
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:12:25 +09:00
3e869a0ec7 web: bump prettier from 3.2.5 to 3.3.0 in /tests/wdio (#9952)
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:12:15 +09:00
7276a416f6 web: bump chromedriver from 125.0.2 to 125.0.3 in /tests/wdio (#9953)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 125.0.2 to 125.0.3.
- [Commits](https://github.com/giggio/node-chromedriver/compare/125.0.2...125.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 23:12:06 +09:00
a989390533 core: groups: optimize recursive children query (#9931) 2024-06-03 11:49:31 +00:00
562c52a48b core, web: update translations (#9938)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-06-03 09:53:18 +09:00
c3cb9bc778 stages/authenticator_webauthn: optimize device types creation (#9932)
* stages/authenticator_webauthn: optimize device types creation

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

* same for aaguid_import task

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-06-01 11:16:55 +02:00
5f65a7c6cc stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#9936)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-06-01 17:17:29 +09:00
95d26563e7 providers/oauth2: don't handle api scope as special scope (#9910)
* providers/oauth2: don't handle api scope as special scope

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

* make default scope selection less magic

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

* ensure missing folder exists

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

* fix duplicate name

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-06-01 17:16:56 +09:00
1cac1492d7 core: bump goauthentik.io/api/v3 from 3.2024042.8 to 3.2024042.9 (#9915)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.8 to 3.2024042.9.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.8...v3.2024042.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-31 18:40:02 +09:00
6c1ac48bd9 web: bump the storybook group in /web with 7 updates (#9917)
Bumps the storybook group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.1.4` | `8.1.5` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.1.4` | `8.1.5` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) | `8.1.4` | `8.1.5` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.1.4` | `8.1.5` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `8.1.4` | `8.1.5` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `8.1.4` | `8.1.5` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.1.4` | `8.1.5` |


Updates `@storybook/addon-essentials` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/addons/essentials)

Updates `@storybook/addon-links` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/addons/links)

Updates `@storybook/blocks` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/ui/blocks)

Updates `@storybook/manager-api` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/lib/manager-api)

Updates `@storybook/web-components` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/frameworks/web-components-vite)

Updates `storybook` from 8.1.4 to 8.1.5
- [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/v8.1.5/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  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>
2024-05-31 18:39:39 +09:00
97f11f7aa8 website/integrations: firezone: remove question mark typo and remove promotional sentence (#9927)
Signed-off-by: 4d62 <ext@4d62.me>
2024-05-31 18:32:06 +09:00
6db763f7dc web: bump @sentry/browser from 8.5.0 to 8.7.0 in /web in the sentry group (#9916)
web: bump @sentry/browser in /web in the sentry group

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


Updates `@sentry/browser` from 8.5.0 to 8.7.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/8.5.0...8.7.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-05-31 18:30:34 +09:00
16b5f692ee core: bump github.com/redis/go-redis/v9 from 9.5.1 to 9.5.2 (#9929)
Bumps [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) from 9.5.1 to 9.5.2.
- [Release notes](https://github.com/redis/go-redis/releases)
- [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/go-redis/compare/v9.5.1...v9.5.2)

---
updated-dependencies:
- dependency-name: github.com/redis/go-redis/v9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-31 18:29:21 +09:00
80c1bd690c core: bump importlib-metadata from 7.0.0 to 7.1.0 (#9928)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](https://github.com/python/importlib_metadata/compare/v7.0.0...v7.1.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-31 18:29:11 +09:00
040dcaa9d6 website/docs: Update ArgoCD integration docs. (#9684)
Co-authored-by: Dylan Kauling <dkauling@armstrongfluidtechnology.com>
2024-05-30 22:34:05 +02:00
66a16752e4 website/integrations: add cloudflare access (#9713)
* init

* rough draft

* main docs

* upd placeholder

* Rebase pull request

rough draft

main docs

upd placeholder

* add to sidebar

* lint

* oops

Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* apply suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <ext@4d62.me>

* upd

Signed-off-by: 4d62 <ext@4d62.me>

---------

Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>
Signed-off-by: 4d62 <ext@4d62.me>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-05-30 11:45:10 -05:00
70c0e1be99 web: bump API Client version (#9913)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-30 02:02:54 +00:00
5beea4624f web/admin: use chips to display permissions/scopes (#9912)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-30 10:43:38 +09:00
50fffa72cc lib/providers/sync: improve outgoing sync (#9835)
* make connection objects not updatable but allow creating with provider

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

* save data returned from google/entra and show it in UI

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

* pass connection object

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

* set immutable id on user automatically

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

* better define transient error codes

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

* format

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

* fix entra

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-30 10:40:10 +09:00
dae4bf0d6b stages/authenticator_validate: fix priority of auth_method being set to auth_webauthn_pwl (#9909)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-30 09:41:21 +09:00
823851652e web: bump esbuild from 0.21.3 to 0.21.4 in /web (#9880)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.21.3 to 0.21.4.
- [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.21.3...v0.21.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:28:12 +09:00
ae7f7c9930 web: bump @wdio/cli from 8.38.0 to 8.38.1 in /tests/wdio in the wdio group (#9902)
web: bump @wdio/cli in /tests/wdio in the wdio group

Bumps the wdio group in /tests/wdio with 1 update: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli).


Updates `@wdio/cli` from 8.38.0 to 8.38.1
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.1/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.1/packages/wdio-cli)

---
updated-dependencies:
- dependency-name: "@wdio/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: wdio
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:28:03 +09:00
5ce4ed4dd3 core: bump structlog from 24.1.0 to 24.2.0 (#9891)
Bumps [structlog](https://github.com/hynek/structlog) from 24.1.0 to 24.2.0.
- [Release notes](https://github.com/hynek/structlog/releases)
- [Changelog](https://github.com/hynek/structlog/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hynek/structlog/compare/24.1.0...24.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:23:25 +09:00
5582cc7745 web: bump @sentry/browser from 8.4.0 to 8.5.0 in /web in the sentry group (#9893)
web: bump @sentry/browser in /web in the sentry group

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


Updates `@sentry/browser` from 8.4.0 to 8.5.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/8.4.0...8.5.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-05-30 06:20:29 +09:00
c384ed5f52 web: bump the storybook group in /web with 7 updates (#9894)
Bumps the storybook group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.1.3` | `8.1.4` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.1.3` | `8.1.4` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) | `8.1.3` | `8.1.4` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.1.3` | `8.1.4` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `8.1.3` | `8.1.4` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `8.1.3` | `8.1.4` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.1.3` | `8.1.4` |


Updates `@storybook/addon-essentials` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/addons/essentials)

Updates `@storybook/addon-links` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/addons/links)

Updates `@storybook/blocks` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/ui/blocks)

Updates `@storybook/manager-api` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/lib/manager-api)

Updates `@storybook/web-components` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/frameworks/web-components-vite)

Updates `storybook` from 8.1.3 to 8.1.4
- [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/v8.1.4/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  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>
2024-05-30 06:19:48 +09:00
02e2ba8971 core: bump github.com/getsentry/sentry-go from 0.27.0 to 0.28.0 (#9890)
Bumps [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go) from 0.27.0 to 0.28.0.
- [Release notes](https://github.com/getsentry/sentry-go/releases)
- [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-go/compare/v0.27.0...v0.28.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:18:38 +09:00
925d5c80df core: bump pydantic from 2.7.1 to 2.7.2 (#9903)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.7.1 to 2.7.2.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.7.1...v2.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:18:06 +09:00
1de69a7bd6 core: bump ruff from 0.4.5 to 0.4.6 (#9904)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.5 to 0.4.6.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.5...v0.4.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:12:23 +09:00
c6979a48e0 core: bump uvicorn from 0.29.0 to 0.30.0 (#9905)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.29.0 to 0.30.0.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.29.0...0.30.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:06:31 +09:00
6e73d60305 core: bump google-api-python-client from 2.130.0 to 2.131.0 (#9906)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.130.0 to 2.131.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.130.0...v2.131.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 06:06:22 +09:00
f388cac07c website/docs: next release notes: add upgrade instructions for postgres to 16 (#9727)
* init

Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* start

* db pass upd

* more

* oops

* simplify setting up v16 service

* cut off pg

* finish

* release breaking changes

* 2024.next

* i need to be less distracted

* rework upgrade instructions

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

* ci: remove postgres 12 tests

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

* spelling

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

* try?

* fix release notes

* iirc theres a danger thing

* typo

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* suggestion

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>

* Update website/docs/troubleshooting/postgres/upgrade_docker.md

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

* prettier

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

* add to sidebar 

Signed-off-by: 4d62 <ext@4d62.me>

* 🙊

Signed-off-by: 4d62 <ext@4d62.me>

* make website

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

---------

Signed-off-by: 4d62 <157558804+4d62ext@users.noreply.github.com>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: 4d62 <ext@4d62.me>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-05-29 15:31:21 +02:00
cf593e5cb9 core: bump coverage from 7.5.2 to 7.5.3 (#9901)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-29 12:08:01 +00:00
c3a98e5d5f translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9861)
* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

* Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-29 13:50:05 +02:00
1048729599 core, web: update translations (#9864)
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-29 13:49:31 +02:00
72442b37e5 website/integrations: capitalize P in WordPress (#9897) 2024-05-29 10:47:07 +00:00
211cdb3a21 website/docs: add docs for configuring Facebook as a source (#9884)
* FB draft

* tweak

* more tweaks

* formatting

* more formatting

* marc edit

---------

Co-authored-by: Tana M Berry <tana@goauthentik.com>
2024-05-28 09:41:26 -05:00
4cca16750e web: bump the esbuild group in /web with 2 updates (#9879)
Bumps the esbuild group in /web with 2 updates: [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) and [@esbuild/linux-arm64](https://github.com/evanw/esbuild).


Updates `@esbuild/darwin-arm64` from 0.21.3 to 0.21.4
- [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.21.3...v0.21.4)

Updates `@esbuild/linux-arm64` from 0.21.3 to 0.21.4
- [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.21.3...v0.21.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:56:35 +09:00
b2d261dd1c core: bump twilio from 9.0.5 to 9.1.0 (#9875)
Bumps [twilio](https://github.com/twilio/twilio-python) from 9.0.5 to 9.1.0.
- [Release notes](https://github.com/twilio/twilio-python/releases)
- [Changelog](https://github.com/twilio/twilio-python/blob/main/CHANGES.md)
- [Commits](https://github.com/twilio/twilio-python/compare/9.0.5...9.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:56:17 +09:00
0663100429 core: bump coverage from 7.5.1 to 7.5.2 (#9876)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.5.1 to 7.5.2.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.5.1...7.5.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:55:47 +09:00
66c3261eeb core: bump goauthentik.io/api/v3 from 3.2024042.7 to 3.2024042.8 (#9877)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.7 to 3.2024042.8.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.7...v3.2024042.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:55:35 +09:00
bf7570bc36 web: bump the babel group in /web with 7 updates (#9878)
Bumps the babel group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) | `7.24.5` | `7.24.6` |
| [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) | `7.24.1` | `7.24.6` |
| [@babel/plugin-transform-private-methods](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-private-methods) | `7.24.1` | `7.24.6` |
| [@babel/plugin-transform-private-property-in-object](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-private-property-in-object) | `7.24.5` | `7.24.6` |
| [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) | `7.24.3` | `7.24.6` |
| [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) | `7.24.5` | `7.24.6` |
| [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) | `7.24.1` | `7.24.6` |


Updates `@babel/core` from 7.24.5 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-core)

Updates `@babel/plugin-proposal-decorators` from 7.24.1 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-plugin-proposal-decorators)

Updates `@babel/plugin-transform-private-methods` from 7.24.1 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-plugin-transform-private-methods)

Updates `@babel/plugin-transform-private-property-in-object` from 7.24.5 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-plugin-transform-private-property-in-object)

Updates `@babel/plugin-transform-runtime` from 7.24.3 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-plugin-transform-runtime)

Updates `@babel/preset-env` from 7.24.5 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-preset-env)

Updates `@babel/preset-typescript` from 7.24.1 to 7.24.6
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-proposal-decorators"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-private-methods"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-private-property-in-object"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-runtime"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: babel
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:55:16 +09:00
20b52d0dbd web: bump glob from 10.4.0 to 10.4.1 in /web (#9881)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.4.0 to 10.4.1.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.4.0...v10.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:55:06 +09:00
a1f5e284c4 web: bump the wdio group in /tests/wdio with 4 updates (#9882)
Bumps the wdio group in /tests/wdio with 4 updates: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli), [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner), [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/cli` from 8.37.0 to 8.38.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.0/packages/wdio-cli)

Updates `@wdio/local-runner` from 8.37.0 to 8.38.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.0/packages/wdio-local-runner)

Updates `@wdio/mocha-framework` from 8.37.0 to 8.38.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.0/packages/wdio-mocha-framework)

Updates `@wdio/spec-reporter` from 8.37.0 to 8.38.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.38.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.38.0/packages/wdio-spec-reporter)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-28 10:54:56 +09:00
0e4737d38f events: fix geoip enrich context not converting to json-seriazable data (#9885) 2024-05-27 17:16:35 +00:00
609b10f7f8 stages/user_login: fix non-json-serializable being used (#9886) 2024-05-27 17:15:45 +00:00
2cff3d15e7 website/docs: improve read replica docs (#9828)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-24 18:26:41 +02:00
4f1d49417c translate: Updates for file locale/en/LC_MESSAGES/django.po in ru (#9859)
Translate locale/en/LC_MESSAGES/django.po in ru

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-24 18:07:31 +02:00
0766a47b4f web: bump API Client version (#9857)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-24 13:54:16 +02:00
bd1ddfebd6 lib/sync/outgoing, sources/ldap: fix sync status endpoint (#9855) 2024-05-24 11:49:22 +00:00
a841743c74 lifecycle/migrate: only acquire lock once (#9856) 2024-05-24 11:36:00 +00:00
0974456ac8 core: add option to select group for property mapping testing (#9834)
* make naming consistent, p1

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

* p2

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

* core: add option to select group for property mapping testing

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-24 13:32:19 +02:00
d44d5a44a1 policies: fix ak_call_policy failing when used in testing (#9853)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-24 13:29:54 +02:00
edf5c8686a website/integrations: gitlab: make placeholder clearer (#9838) 2024-05-24 13:16:07 +02:00
70ace8b209 core: bump requests from 2.31.0 to 2.32.2 (#9852)
Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.2.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.31.0...v2.32.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:53:16 +02:00
c3509e63af core: bump codespell from 2.2.6 to 2.3.0 (#9842)
* core: bump codespell from 2.2.6 to 2.3.0

Bumps [codespell](https://github.com/codespell-project/codespell) from 2.2.6 to 2.3.0.
- [Release notes](https://github.com/codespell-project/codespell/releases)
- [Commits](https://github.com/codespell-project/codespell/compare/v2.2.6...v2.3.0)

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

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

* fix

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-24 12:53:05 +02:00
89b8206176 core: bump docker from 7.0.0 to 7.1.0 (#9843)
Bumps [docker](https://github.com/docker/docker-py) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/docker/docker-py/releases)
- [Commits](https://github.com/docker/docker-py/compare/7.0.0...7.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:27:24 +02:00
908d87c142 translate: Updates for file web/xliff/en.xlf in zh-Hans (#9839)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-24 12:24:39 +02:00
4ab4e81fb0 translate: Updates for file web/xliff/en.xlf in zh_CN (#9840)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-24 12:24:24 +02:00
6dae1a4fe7 core: bump watchdog from 4.0.0 to 4.0.1 (#9845)
Bumps [watchdog](https://github.com/gorakhargosh/watchdog) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/gorakhargosh/watchdog/releases)
- [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst)
- [Commits](https://github.com/gorakhargosh/watchdog/compare/v4.0.0...v4.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:23:50 +02:00
d11de73e95 website: bump @types/react from 18.3.2 to 18.3.3 in /website (#9841)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.2 to 18.3.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:23:39 +02:00
b08fb5fdf1 core: bump sentry-sdk from 2.2.1 to 2.3.1 (#9844)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.2.1 to 2.3.1.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.2.1...2.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:23:18 +02:00
3c9e8c7287 core: bump goauthentik.io/api/v3 from 3.2024042.4 to 3.2024042.7 (#9846)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.4 to 3.2024042.7.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.4...v3.2024042.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:23:07 +02:00
691d0be41e web: bump chromedriver from 125.0.1 to 125.0.2 in /tests/wdio (#9847)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 125.0.1 to 125.0.2.
- [Commits](https://github.com/giggio/node-chromedriver/compare/125.0.1...125.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:22:56 +02:00
dfbaccbab6 web: bump @sentry/browser from 8.3.0 to 8.4.0 in /web in the sentry group (#9848)
web: bump @sentry/browser in /web in the sentry group

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


Updates `@sentry/browser` from 8.3.0 to 8.4.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/8.3.0...8.4.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-05-24 12:22:48 +02:00
f3bdb189f6 web: bump the storybook group in /web with 7 updates (#9849)
Bumps the storybook group in /web with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.1.2` | `8.1.3` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.1.2` | `8.1.3` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) | `8.1.2` | `8.1.3` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.1.2` | `8.1.3` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `8.1.2` | `8.1.3` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `8.1.2` | `8.1.3` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.1.2` | `8.1.3` |


Updates `@storybook/addon-essentials` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/addons/essentials)

Updates `@storybook/addon-links` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/addons/links)

Updates `@storybook/blocks` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/ui/blocks)

Updates `@storybook/manager-api` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/lib/manager-api)

Updates `@storybook/web-components` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/frameworks/web-components-vite)

Updates `storybook` from 8.1.2 to 8.1.3
- [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/v8.1.3/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  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>
2024-05-24 12:22:40 +02:00
85b3523639 web: bump ts-pattern from 5.1.1 to 5.1.2 in /web (#9850)
Bumps [ts-pattern](https://github.com/gvergnaud/ts-pattern) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gvergnaud/ts-pattern/releases)
- [Commits](https://github.com/gvergnaud/ts-pattern/compare/v5.1.1...v5.1.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:22:28 +02:00
9ff61a7120 web: bump glob from 10.3.16 to 10.4.0 in /web (#9851)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.16 to 10.4.0.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.16...v10.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-24 12:22:18 +02:00
f742b986a7 core, web: update translations (#9837) 2024-05-24 02:42:44 +02:00
177bdfa689 web: bump API Client version (#9833)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-23 17:58:18 +00:00
c3445374c2 core: FIPS (#9683)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-05-23 17:34:52 +00:00
c2da6822dc translate: Updates for file web/xliff/en.xlf in zh-Hans (#9831)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-23 18:38:49 +02:00
493294ef9f translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9829)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-23 18:38:34 +02:00
17f807e8b0 translate: Updates for file web/xliff/en.xlf in zh_CN (#9830)
Translate web/xliff/en.xlf in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-23 18:38:20 +02:00
96eb98500c translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9832)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-23 18:38:07 +02:00
ddd75f6d09 lib/sync: fix json error (#9827)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-23 17:37:39 +02:00
fbad02fac1 providers/scim, sources/ldap: switch to using postgres advisory locks instead of redis locks (#9511)
* providers/scim, sources/ldap: switch to using postgres advisory locks instead of redis locks

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

* website/integrations: discord: fix typo

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

* fix timeout logic

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

* remove redis locks completely

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

* Apply suggestions from code review

Signed-off-by: Jens L. <jens@beryju.org>

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Jens L <jens@goauthentik.io>
2024-05-23 13:41:42 +02:00
fbab822db1 core: bump ruff from 0.4.4 to 0.4.5 (#9819)
* core: bump ruff from 0.4.4 to 0.4.5

Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.4 to 0.4.5.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.4...v0.4.5)

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

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

* format

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-23 13:12:52 +02:00
d8316eea9b website/integrations: compress images (#9826)
[create-pull-request] automated change

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-23 13:12:42 +02:00
8182c9f7c2 web: bump @sentry/browser from 8.2.1 to 8.3.0 in /web in the sentry group (#9821)
web: bump @sentry/browser in /web in the sentry group

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


Updates `@sentry/browser` from 8.2.1 to 8.3.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/8.2.1...8.3.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-05-23 12:48:48 +02:00
5d94b97e97 sources/oauth: modernizes discord icon (#9817)
* sources: modernize discord icon

* rm size specification
2024-05-23 12:48:09 +02:00
35ddbb6d75 website/integrations: update portainer screenshot (#9818) 2024-05-23 12:43:49 +02:00
2b8bc38fc3 core, web: update translations (#9816)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-23 12:28:38 +02:00
9b0b504531 core: bump google-api-python-client from 2.129.0 to 2.130.0 (#9820)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.129.0 to 2.130.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.129.0...v2.130.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-23 12:26:51 +02:00
c312430007 web: bump chromedriver from 125.0.0 to 125.0.1 in /tests/wdio (#9823)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 125.0.0 to 125.0.1.
- [Commits](https://github.com/giggio/node-chromedriver/compare/125.0.0...125.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-23 12:26:39 +02:00
4e65c205e3 web: bump the rollup group in /web with 3 updates (#9824)
Bumps the rollup group in /web with 3 updates: [@rollup/rollup-darwin-arm64](https://github.com/rollup/rollup), [@rollup/rollup-linux-arm64-gnu](https://github.com/rollup/rollup) and [@rollup/rollup-linux-x64-gnu](https://github.com/rollup/rollup).


Updates `@rollup/rollup-darwin-arm64` from 4.17.2 to 4.18.0
- [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.17.2...v4.18.0)

Updates `@rollup/rollup-linux-arm64-gnu` from 4.17.2 to 4.18.0
- [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.17.2...v4.18.0)

Updates `@rollup/rollup-linux-x64-gnu` from 4.17.2 to 4.18.0
- [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.17.2...v4.18.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-23 12:26:11 +02:00
372a66c876 web: bump the wdio group in /tests/wdio with 4 updates (#9822)
Bumps the wdio group in /tests/wdio with 4 updates: [@wdio/cli](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-cli), [@wdio/local-runner](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-local-runner), [@wdio/mocha-framework](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-mocha-framework) and [@wdio/spec-reporter](https://github.com/webdriverio/webdriverio/tree/HEAD/packages/wdio-spec-reporter).


Updates `@wdio/cli` from 8.36.1 to 8.37.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.37.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.37.0/packages/wdio-cli)

Updates `@wdio/local-runner` from 8.36.1 to 8.37.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.37.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.37.0/packages/wdio-local-runner)

Updates `@wdio/mocha-framework` from 8.36.1 to 8.37.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.37.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.37.0/packages/wdio-mocha-framework)

Updates `@wdio/spec-reporter` from 8.36.1 to 8.37.0
- [Release notes](https://github.com/webdriverio/webdriverio/releases)
- [Changelog](https://github.com/webdriverio/webdriverio/blob/v8.37.0/CHANGELOG.md)
- [Commits](https://github.com/webdriverio/webdriverio/commits/v8.37.0/packages/wdio-spec-reporter)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-23 12:25:58 +02:00
3630349388 web/admin: add link to enterprise docs on license page (#9811)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-22 22:34:03 +02:00
347746cbcd enterprise/stages/source: fix error when creating source stage from blueprint (#9810)
* enterprise/stages/source: fix error when creating source stage from blueprint

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

* fix error for pretend user

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

* fix unrelated frontend bug

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

* cleanup

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-22 18:12:31 +02:00
ef2e1ad27b ci: use container registry for container build cache (#9809)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-05-22 13:03:08 +02:00
8a6b34eb5c core: bump lxml from 5.2.1 to 5.2.2 (#9717)
Bumps [lxml](https://github.com/lxml/lxml) from 5.2.1 to 5.2.2.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-5.2.1...lxml-5.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:40:36 +02:00
26f72bcac4 web: bump mermaid from 10.9.0 to 10.9.1 in /web (#9734)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.9.0 to 10.9.1.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/v10.9.0...v10.9.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:40:25 +02:00
f04466b3be core: bump scim2-filter-parser from 0.5.0 to 0.5.1 (#9730)
Bumps [scim2-filter-parser](https://github.com/15five/scim2-filter-parser) from 0.5.0 to 0.5.1.
- [Changelog](https://github.com/15five/scim2-filter-parser/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/15five/scim2-filter-parser/commits)

---
updated-dependencies:
- dependency-name: scim2-filter-parser
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:38:57 +02:00
4ba53d2f08 web: bump core-js from 3.37.0 to 3.37.1 in /web (#9733)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.37.0 to 3.37.1.
- [Release notes](https://github.com/zloirock/core-js/releases)
- [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/zloirock/core-js/commits/v3.37.1/packages/core-js)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:38:46 +02:00
7a13046a27 stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#9729)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-22 11:38:35 +02:00
939e2c1edd translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9802)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-22 11:25:45 +02:00
cf06b4177a translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9803)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-22 11:25:29 +02:00
f8079d63fa core: bump sentry-sdk from 2.2.0 to 2.2.1 (#9807)
---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:24:59 +02:00
576a56c562 web: bump the storybook group in /web with 7 updates (#9804)
---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  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>
2024-05-22 11:24:42 +02:00
cf9b14213e web: bump glob from 10.3.15 to 10.3.16 in /web (#9805)
---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:24:34 +02:00
73cbdb77ed root: docker-compose: remove version top level element (#9631)
.
2024-05-22 11:24:16 +02:00
fd66be9fa2 core, web: update translations (#9790)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-22 02:49:11 +02:00
96bf9ee898 web: bump API Client version (#9801)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-22 02:49:00 +02:00
6c4c535d57 web/admin: rework initial wizard pages and add grid layout (#9668)
* remove @goauthentik/authentik as TS path

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

* initial implementation

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

* oh yeah

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

* format earlier changes

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

* support plain alert

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

* initial attempt at dedupe

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

* make it a base class

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

* migrate all wizards

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

* create type create mixin to dedupe more, add icon to source create

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

* add ldap icon

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

* Optimised images with calibre/image-actions

* match inverting

we should probably replace all icons with coloured ones so we don't need to invert them...I guess

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

* format

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

* make everything more explicit

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

* add icons to provider

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

* add remaining provider icons

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

* rework to not use inheritance

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

* fix unrelated typo

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

* make app wizard use grid layout

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

* keep wizard height consistent

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>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-22 02:41:33 +02:00
0ed4bba5a5 website/integrations: discord: fix typo (#9800)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-05-22 01:51:24 +02:00
6e31e5b889 website/integration/netbox: fix group custom pipeline example (#9738)
website/integration/netbox fix group custom pipeline example

Signed-off-by: ThysTips <contact@antoinethys.com>
2024-05-21 20:19:14 +02:00
a5467c6e19 root: add primary-replica db router (#9479)
* root: add primary-replica db router

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

* copy all settings for database replicas

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

* refresh read replicas config, switch to using a dict instead of a list for easier refresh

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

* add test for get_keys

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

* fix getting override

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

* lint

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

* nosec

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

* small fixes

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

* fix replica settings

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

* generate config: add a dummy read replica

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

* add doc

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

* add healthchecks for replicas

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

* fix

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

* add note about hot reloading

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-05-21 20:15:49 +02:00
09832355e3 website/integrations: add three more policy-expressions to discord-docs (#5760)
* - Add two policy-examples for syncing discord roles to authentik groups during enrollment or authentication
- Add policy to store oauth-info and create an authentik-avatar-attribute during enrollment or authentication

* Fix issues and lint

- Fixed issue with wrong return during provider-check
- Lint using black

Signed-off-by: Keyinator <k3yinator@gmail.com>

* Fix capitalization and punctuation

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Keyinator <k3yinator@gmail.com>

* Fix documentation link and add explanation to MAPPED_ROLES attribute

---------

Signed-off-by: Keyinator <k3yinator@gmail.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2024-05-21 13:10:16 -05:00
6ffef878f0 website/integrations: netbox: add missing scope configuration (#9491) 2024-05-21 10:42:08 +00:00
644090dc58 website/docs: remove marketing from skyhigh (#9666)
* Update index.md

removing advertising copy from documentation

Signed-off-by: jsiegel-supplyframe <120349776+jsiegel-supplyframe@users.noreply.github.com>

* fix formatting

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

---------

Signed-off-by: jsiegel-supplyframe <120349776+jsiegel-supplyframe@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-21 01:08:06 +02:00
d07508b9a4 Fix: Application Search text color should be light in dark mode (#9788)
Fixes #8707
2024-05-20 16:55:16 -05:00
44d7e81a93 providers/scim: optimize PropertyMapping fetching and execution (#9689)
* add helper to mass-compile and re-use mappings

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

* implement for scim

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

* actually make it even simpler

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

* migrate google

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

* migrate microsoft too

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

* remove sleeps in tests with freezegun

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

* migrate ldap to propertymapping helper

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

* move mapper to generic sync

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

* fix

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

* apparently that doesn't work

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

* forgot a sleep

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

* backport fixes from #9783

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 15:37:22 +02:00
2e91b9d035 web: bump API Client version (#9785)
* web: bump API Client version

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* don't include users in group calls

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

---------

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 13:13:45 +00:00
964c6a1050 core: fix auth_method and auth_method_args being overwritten by password stage (#9782)
the assumption used to be that the password stage would always be first, but that doesn't have to be the case

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 15:03:25 +02:00
90a1c5ab85 core: add include_users flag to single group retrieval (#9721)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 14:47:00 +02:00
8162c1ec86 enterprise/providers/google_workspace: ensure no group is created when no property mappings are set (#9783)
* enterprise/providers/google_workspace: ensure no group is created when no property mappings are set

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

* add tests for that to all sync providers

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 14:16:34 +02:00
ab46610d9b core: bump tenant-schemas-celery from 2.2.0 to 3.0.0 (#9773)
Bumps [tenant-schemas-celery](https://github.com/maciej-gol/tenant-schemas-celery) from 2.2.0 to 3.0.0.
- [Release notes](https://github.com/maciej-gol/tenant-schemas-celery/releases)
- [Commits](https://github.com/maciej-gol/tenant-schemas-celery/compare/2.2.0...3.0.0)

---
updated-dependencies:
- dependency-name: tenant-schemas-celery
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:02:07 +02:00
6909b58279 core: bump pdoc from 14.4.0 to 14.5.0 (#9772)
Bumps [pdoc](https://github.com/mitmproxy/pdoc) from 14.4.0 to 14.5.0.
- [Changelog](https://github.com/mitmproxy/pdoc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mitmproxy/pdoc/compare/v14.4.0...v14.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:01:11 +02:00
6d7a06227f core: bump pytest from 8.2.0 to 8.2.1 (#9774)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.2.0 to 8.2.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.2.0...8.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:01:00 +02:00
1459a13991 web: bump chart.js from 4.4.2 to 4.4.3 in /web (#9776)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.2...v4.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:00:43 +02:00
1921ce39f6 web: bump @formatjs/intl-listformat from 7.5.5 to 7.5.7 in /web (#9777)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.5.5 to 7.5.7.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.5.5...@formatjs/intl-listformat@7.5.7)

---
updated-dependencies:
- dependency-name: "@formatjs/intl-listformat"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:00:34 +02:00
263cff6393 core: bump github.com/sethvargo/go-envconfig from 1.0.2 to 1.0.3 (#9778)
Bumps [github.com/sethvargo/go-envconfig](https://github.com/sethvargo/go-envconfig) from 1.0.2 to 1.0.3.
- [Release notes](https://github.com/sethvargo/go-envconfig/releases)
- [Commits](https://github.com/sethvargo/go-envconfig/compare/v1.0.2...v1.0.3)

---
updated-dependencies:
- dependency-name: github.com/sethvargo/go-envconfig
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 13:00:26 +02:00
5a61688472 rbac: fix some rough edges and inconsistencies (#9771)
rbac: fix some rough edges

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-20 12:52:27 +02:00
919b56c466 web/admin: fix entra provider mapping form (#9767)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-18 14:57:16 +02:00
db7bc8b7ad Change custom pipeline since netbox 4.0.0 (#9743)
Due to a Django 5.0 regression, group.user_set does not exist anymore.  
https://github.com/netbox-community/netbox/discussions/16098
https://github.com/netbox-community/netbox/discussions/16000

Signed-off-by: Manuel Rais <manuel.rais@nousvoir.com>
2024-05-17 17:04:41 -05:00
5768cb5858 web: bump @sentry/browser from 7.114.0 to 8.2.1 in /web in the sentry group across 1 directory (#9757)
* 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 7.114.0 to 8.2.1
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.114.0...8.2.1)

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

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

* fix sentry beforeSend

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-17 12:43:33 +02:00
5b77bc33c7 core, web: update translations (#9714)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-17 12:32:05 +02:00
93650e925a core: bump sentry-sdk from 2.1.1 to 2.2.0 (#9753)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.1.1...2.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 12:19:49 +02:00
83823068fe core: bump selenium from 4.20.0 to 4.21.0 (#9754)
Bumps [selenium](https://github.com/SeleniumHQ/Selenium) from 4.20.0 to 4.21.0.
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/compare/selenium-4.20.0...selenium-4.21.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 12:18:22 +02:00
d922f41438 core: bump msgraph-sdk from 1.2.0 to 1.4.0 (#9755)
Bumps [msgraph-sdk](https://github.com/microsoftgraph/msgraph-sdk-python) from 1.2.0 to 1.4.0.
- [Release notes](https://github.com/microsoftgraph/msgraph-sdk-python/releases)
- [Changelog](https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/CHANGELOG.md)
- [Commits](https://github.com/microsoftgraph/msgraph-sdk-python/compare/v1.2.0...v1.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 12:18:11 +02:00
ef3d545d7a core: bump github.com/sethvargo/go-envconfig from 1.0.1 to 1.0.2 (#9756)
Bumps [github.com/sethvargo/go-envconfig](https://github.com/sethvargo/go-envconfig) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/sethvargo/go-envconfig/releases)
- [Commits](https://github.com/sethvargo/go-envconfig/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: github.com/sethvargo/go-envconfig
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 12:17:55 +02:00
e9efbb2161 web: bump chromedriver from 124.0.3 to 125.0.0 in /tests/wdio (#9758)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 124.0.3 to 125.0.0.
- [Commits](https://github.com/giggio/node-chromedriver/compare/124.0.3...125.0.0)

---
updated-dependencies:
- dependency-name: chromedriver
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-17 12:17:37 +02:00
a3634ab21d website/docs: new PR for the Entra provider docs (ignore old one) (#9741)
* new PR

* ken edits

* tweaks

---------

Co-authored-by: Tana M Berry <tana@goauthentik.com>
2024-05-16 17:00:03 -05:00
f28209548b root: include task_id in events and logs (#9749)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-16 13:33:00 +02:00
9589e04072 web: bump the esbuild group in /web with 2 updates (#9745)
Bumps the esbuild group in /web with 2 updates: [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) and [@esbuild/linux-arm64](https://github.com/evanw/esbuild).


Updates `@esbuild/darwin-arm64` from 0.21.2 to 0.21.3
- [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.21.2...v0.21.3)

Updates `@esbuild/linux-arm64` from 0.21.2 to 0.21.3
- [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.21.2...v0.21.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 12:23:20 +02:00
6490703ba3 web: bump esbuild from 0.21.2 to 0.21.3 in /web (#9746)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.21.2 to 0.21.3.
- [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.21.2...v0.21.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 12:23:06 +02:00
ca2fce05f5 web: bump the storybook group across 1 directory with 7 updates (#9747)
Bumps the storybook group with 6 updates in the /web directory:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.0.10` | `8.1.1` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.0.10` | `8.1.1` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.0.10` | `8.1.1` |
| [@storybook/web-components](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/web-components) | `8.0.10` | `8.1.1` |
| [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) | `8.0.10` | `8.1.1` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.0.10` | `8.1.1` |



Updates `@storybook/addon-essentials` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/addons/essentials)

Updates `@storybook/addon-links` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/addons/links)

Updates `@storybook/blocks` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/ui/blocks)

Updates `@storybook/manager-api` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/lib/manager-api)

Updates `@storybook/web-components` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/renderers/web-components)

Updates `@storybook/web-components-vite` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/frameworks/web-components-vite)

Updates `storybook` from 8.0.10 to 8.1.1
- [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/v8.1.1/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/web-components"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/web-components-vite"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 12:22:54 +02:00
9dc813d9ab website/docs: update traefik to latest version in proxy provider (#9707) 2024-05-13 20:00:37 +00:00
833c66a9dd sources/saml: fix FlowPlanner error due to pickle (#9708)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-13 20:33:34 +02:00
5d54f696d4 website/docs: add docs about Google Workspace (#9669)
* stub files

* tweaks

* add to sidebar

* tweaks

* steps to set up gws

* first drafts

* link

* unsaved

* formatting

* typos

* add Ent badge

* backchannel and otehr edits

* tweaks

* tweaks

* rewrite stuff

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

* em one word

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-05-13 20:33:06 +02:00
0746652995 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9702)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-13 19:36:53 +02:00
a1a55c644a translate: Updates for file web/xliff/en.xlf in zh_CN (#9703)
* Translate web/xliff/en.xlf in zh_CN

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

* Translate web/xliff/en.xlf in zh_CN

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-13 19:36:38 +02:00
fce57d258e translate: Updates for file web/xliff/en.xlf in zh-Hans (#9705)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-13 19:36:14 +02:00
17e30b7adc translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9706)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-13 19:35:58 +02:00
c5b3f8a578 web: bump esbuild from 0.21.1 to 0.21.2 in /web (#9696) 2024-05-13 13:41:46 +02:00
3a3619fa18 web: bump chromedriver from 124.0.2 to 124.0.3 in /tests/wdio (#9692)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 124.0.2 to 124.0.3.
- [Commits](https://github.com/giggio/node-chromedriver/compare/124.0.2...124.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:41:12 +02:00
21b933efff website: bump @types/react from 18.3.1 to 18.3.2 in /website (#9691)
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.1 to 18.3.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:40:38 +02:00
97fc2cba69 core, web: update translations (#9672)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rissson <18313093+rissson@users.noreply.github.com>
2024-05-13 11:40:14 +02:00
7ef627d476 core: bump psycopg from 3.1.18 to 3.1.19 (#9698)
Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.1.18 to 3.1.19.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.1.18...3.1.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:36:47 +02:00
d16c603499 core: bump google-api-python-client from 2.128.0 to 2.129.0 (#9694)
Bumps [google-api-python-client](https://github.com/googleapis/google-api-python-client) from 2.128.0 to 2.129.0.
- [Release notes](https://github.com/googleapis/google-api-python-client/releases)
- [Changelog](https://github.com/googleapis/google-api-python-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-python-client/compare/v2.128.0...v2.129.0)

---
updated-dependencies:
- dependency-name: google-api-python-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:35:37 +02:00
99a69bb52f web: bump the esbuild group in /web with 2 updates (#9695)
Bumps the esbuild group in /web with 2 updates: [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) and [@esbuild/linux-arm64](https://github.com/evanw/esbuild).


Updates `@esbuild/darwin-arm64` from 0.21.1 to 0.21.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.21.1...v0.21.2)

Updates `@esbuild/linux-arm64` from 0.21.1 to 0.21.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.21.1...v0.21.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:35:10 +02:00
ac8192d660 web: bump glob from 10.3.14 to 10.3.15 in /web (#9697)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.14 to 10.3.15.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.14...v10.3.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:34:50 +02:00
cdf3449230 core: bump freezegun from 1.5.0 to 1.5.1 (#9693)
Bumps [freezegun](https://github.com/spulec/freezegun) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/spulec/freezegun/releases)
- [Changelog](https://github.com/spulec/freezegun/blob/master/CHANGELOG)
- [Commits](https://github.com/spulec/freezegun/compare/1.5.0...1.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 11:33:24 +02:00
ef2a40ed7d web: fix value handling inside controlled components (#9648)
* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: fix value handling inside controlled components

This is one of those stupid bugs that drive web developers crazy. The basics are straightforward:
when you cause a higher-level component to have a "big enough re-render," for some unknown
definition of "big enough," it will re-render the sub-components. In traditional web interaction,
those components should never be re-rendered while the user is interacting with the form, but in
frameworks where there's dynamic re-arrangement, part or all of the form could get re-rendered at
any mmoment. Since neither the form nor any of its intermediaries is tracking the values as they're
changed, it's up to the components themselves to keep the user's input-- and to be hardened against
property changes coming from the outside world.

So static memoization of the initial value passed in, and aggressively walling off the values the
customer generates from that field, are needed to protect the user's work from any framework's
dynamic DOM management. I remember struggling with this in React; I had hoped Lit was better, but in
this case, not better enough.

The protocol for "is it an ak-data-control" is "it has a `json()` method that returns the data ready
to be sent to the authentik server."  I missed that in one place, so that's on me.

* Eslint had opinions.

* Added comments to explain something.
2024-05-10 09:50:07 -07:00
09cacbd76b website/docs: add hardening advice and link directly to Cure53 results (#9670)
docs: add hardening advice and link directly to Cure53 results

Signed-off-by: Fletcher Heisler <fheisler@users.noreply.github.com>
2024-05-10 09:07:19 -04:00
cb33f0d1e2 core: bump goauthentik.io/api/v3 from 3.2024042.2 to 3.2024042.4 (#9674)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2024042.2 to 3.2024042.4.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2024042.2...v3.2024042.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:13:36 +02:00
90af4b29a6 core: bump ruff from 0.4.3 to 0.4.4 (#9677)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.3...v0.4.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:13:26 +02:00
6b9158591e core: bump github.com/prometheus/client_golang from 1.19.0 to 1.19.1 (#9675)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.19.0 to 1.19.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:13:16 +02:00
9c15cda191 web: bump glob from 10.3.12 to 10.3.14 in /web (#9676)
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.12 to 10.3.14.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.12...v10.3.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-10 12:13:05 +02:00
046b8d5cbf lib/providers/sync: multiple minor fixes (#9667)
* don't propagate from signal

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

* improve retry

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-09 22:02:13 +02:00
8b74b83983 core: fix source flow_manager not always appending save stage (#9659)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-09 19:04:32 +02:00
8de038b387 web: bump API Client version (#9660)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-09 16:33:56 +00:00
2edc651582 web/admin: only show non-backchannel providers in application provider select (#9658)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-09 18:10:07 +02:00
85594a119c website/docs: add new doc about extra steps for hardening authentik (#9649)
* add to sidebar

* tweaks

* tweaks

* add derek edit

* ken edit

* Update website/docs/security/security-hardening.md

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

* tweaks

---------

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Tana M Berry <tana@goauthentik.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
2024-05-09 10:02:51 -05:00
1a97ccea03 web: bump API Client version (#9656) 2024-05-09 15:46:01 +02:00
99ad492951 enterprise/providers/microsoft_entra: initial account sync to microsoft entra (#9632)
* initial

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

* add entra mappings

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

* fix some stuff

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

* make API endpoints more consistent

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

* implement more things

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

* add user tests

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

* fix most group tests + fix bugs

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

* more group tests, fix bugs

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

* fix missing __init__

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

* add ui for provisioned users

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

* fix a bunch of bugs

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

* add `creating` to property mapping env

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

* always sync group members

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

* fix stuff

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

* fix group membership

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

* fix some types

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

* fix tests

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

* add group member add test

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

* create sync status component to dedupe

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

* fix discovery tests

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

* get rid of more code and fix more issues

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

* add error handling for auth and transient

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

* make sure autoretry is on

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

* format web

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

* wait for task in signal

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

* fix tests

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

* add squashed google migration

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-05-09 15:41:23 +02:00
ff4ec6f9b4 web: bump chromedriver from 124.0.1 to 124.0.2 in /tests/wdio (#9652)
Bumps [chromedriver](https://github.com/giggio/node-chromedriver) from 124.0.1 to 124.0.2.
- [Commits](https://github.com/giggio/node-chromedriver/compare/124.0.1...124.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-09 13:10:02 +02:00
0c49de67b8 web: bump @sentry/browser from 7.113.0 to 7.114.0 in /web in the sentry group (#9653)
web: bump @sentry/browser in /web in the sentry group

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


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

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2024-05-09 13:09:52 +02:00
0d73528ec7 core, web: update translations (#9650) 2024-05-09 02:48:01 +02:00
80ca4e5722 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh_CN (#9644)
Translate locale/en/LC_MESSAGES/django.po in zh_CN

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-08 23:28:02 +02:00
1ebe200a46 translate: Updates for file web/xliff/en.xlf in zh-Hans (#9646)
Translate web/xliff/en.xlf in zh-Hans

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

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-08 23:27:55 +02:00
5683c81f27 translate: Updates for file web/xliff/en.xlf in zh_CN (#9645)
* Translate web/xliff/en.xlf in zh_CN

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

* Translate web/xliff/en.xlf in zh_CN

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

---------

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-08 23:27:24 +02:00
f0477309d5 translate: Updates for file locale/en/LC_MESSAGES/django.po in zh-Hans (#9647)
Translate django.po in zh-Hans

100% translated source file: 'django.po'
on 'zh-Hans'.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2024-05-08 23:27:12 +02:00
465820b002 Merge branch 'main' into 5165-password-strength-indicator
* main: (160 commits)
  website: update hackathon with prize pool (#6170)
  web: bump @babel/plugin-transform-runtime from 7.22.6 to 7.22.7 in /web (#6166)
  web: bump @babel/core from 7.22.6 to 7.22.7 in /web (#6165)
  web: bump @babel/plugin-proposal-decorators from 7.22.6 to 7.22.7 in /web (#6167)
  web: bump @babel/preset-env from 7.22.6 to 7.22.7 in /web (#6168)
  website: bump prettier from 2.8.8 to 3.0.0 in /website (#6155)
  web: bump storybook from 7.0.25 to 7.0.26 in /web (#6162)
  core: bump goauthentik.io/api/v3 from 3.2023054.2 to 3.2023054.4 (#6154)
  core: bump golang.org/x/oauth2 from 0.9.0 to 0.10.0 (#6153)
  web: bump @storybook/addon-essentials from 7.0.25 to 7.0.26 in /web (#6158)
  ci: bump actions/setup-node from 3.6.0 to 3.7.0 (#6156)
  web: bump core-js from 3.31.0 to 3.31.1 in /web (#6160)
  web: bump @storybook/addon-links from 7.0.25 to 7.0.26 in /web (#6159)
  web: bump @storybook/web-components-vite from 7.0.25 to 7.0.26 in /web (#6163)
  web: bump lit from 2.7.5 to 2.7.6 in /web (#6161)
  core: bump lxml from 4.9.2 to 4.9.3 (#6151)
  web: bump @babel/core from 7.22.5 to 7.22.6 in /web (#6143)
  web: bump @babel/plugin-transform-runtime from 7.22.5 to 7.22.6 in /web (#6142)
  web: bump @babel/preset-env from 7.22.5 to 7.22.6 in /web (#6144)
  web: bump @babel/plugin-proposal-decorators from 7.22.5 to 7.22.6 in /web (#6141)
  ...
2023-07-06 08:05:05 -07:00
a75c9434d9 Merge branch 'main' into 5165-password-strength-indicator
* main: (23 commits)
  web: bump API Client version (#5935)
  sources/ldap: add support for cert based auth (#5850)
  ci: replace status with state for auto-deployment
  ci: don't write CI status to file
  ci: add workflow to automatically update next branch (#5921)
  providers/ldap: fix Outpost provider listing excluding backchannel providers (#5933)
  root: revert to use secret_key for JWT signing (#5934)
  sources/ldap: fix duplicate bind when authenticating user directly to… (#5927)
  web: bump core-js from 3.30.2 to 3.31.0 in /web (#5928)
  core: bump pytest from 7.3.1 to 7.3.2 (#5929)
  web: bump @rollup/plugin-commonjs from 25.0.0 to 25.0.1 in /web (#5931)
  web: bump @formatjs/intl-listformat from 7.3.0 to 7.4.0 in /web (#5932)
  core: bump github.com/go-ldap/ldap/v3 from 3.4.4 to 3.4.5 (#5930)
  website/integrations: Fix header in dokuwiki instructions (#5926)
  providers/oauth2: launch url: if URL parsing fails, return no launch URL (#5918)
  web: bump @babel/core from 7.22.1 to 7.22.5 in /web (#5909)
  web: bump @babel/plugin-proposal-decorators from 7.22.3 to 7.22.5 in /web (#5910)
  web: bump @babel/preset-typescript from 7.21.5 to 7.22.5 in /web (#5912)
  web: bump @babel/preset-env from 7.22.4 to 7.22.5 in /web (#5915)
  core: bump requests-mock from 1.10.0 to 1.11.0 (#5911)
  ...
2023-06-12 09:55:35 -07:00
4ea9b69ab5 web: fix out-of-date comment 2023-06-08 14:38:45 -07:00
c48eee0ebf web: add visualizing and testing for the FieldRenderers 2023-06-08 13:43:13 -07:00
0d94373f10 web: password quality indicators
Resolves issue 5165

This commit updates the password match indicator so that the user, and not
the component, makes decisions about the names of the initial and confirmation
inputs.
2023-06-08 11:25:13 -07:00
1c85dc512f Merge branch 'main' into 5165-password-strength-indicator
* main:
  providers/ldap: rework Schema and DSE (#5838)
  web/flows: update default flow background (#5905)
  web: bump @formatjs/intl-listformat from 7.2.2 to 7.3.0 in /web (#5866)
  website/integrations: add account linking note for WriteFreely (#5804)
  web: bump @storybook/addon-essentials from 7.0.18 to 7.0.20 in /web (#5894)
  web: bump @storybook/web-components-vite from 7.0.18 to 7.0.20 in /web (#5895)
  web: bump @storybook/blocks from 7.0.18 to 7.0.20 in /web (#5893)
  web: bump storybook from 7.0.18 to 7.0.20 in /web (#5896)
  website/docs: correct LDAP StartTLS documentation (#5886)
  core: bump python from 3.11.3-slim-bullseye to 3.11.4-slim-bullseye (#5891)
  ci: bump docker/setup-qemu-action from 2.1.0 to 2.2.0 (#5892)
  core: bump selenium from 4.9.1 to 4.10.0 (#5897)
  web: bump pyright from 1.1.312 to 1.1.313 in /web (#5898)
  web: bump @storybook/addon-links from 7.0.18 to 7.0.20 in /web (#5899)
  web: bump @storybook/web-components from 7.0.18 to 7.0.20 in /web (#5900)
  core: bump urllib3 from 2.0.2 to 2.0.3 (#5901)
  core: bump ruff from 0.0.271 to 0.0.272 (#5902)
  core: bump sentry-sdk from 1.25.0 to 1.25.1 (#5903)
2023-06-08 08:42:11 -07:00
a71778651f web: improve password experience
This commit disassembles PromptStage and places function that don't
need a reference to the PromptStage object into a collection of
maps between the Stage type and the prompt associated with it.  (In
a better world, this would be a great place to try some post-Midgard
mplementation of itemtype/itemid/itemprop).

This surfaced the nature of the relationship between Password and
Password (Repeat), allowing us to modify both to show password
strength and password matching for the "change password" dialog.
2023-06-08 08:35:23 -07:00
373 changed files with 23893 additions and 10659 deletions

View File

@ -1,5 +1,3 @@
version: "3.7"
services:
postgresql:
image: docker.io/library/postgres:${PSQL_TAG:-16}

View File

@ -4,3 +4,4 @@ hass
warmup
ontext
singed
assertIn

View File

@ -50,7 +50,6 @@ jobs:
fail-fast: false
matrix:
psql:
- 12-alpine
- 15-alpine
- 16-alpine
steps:
@ -104,7 +103,6 @@ jobs:
fail-fast: false
matrix:
psql:
- 12-alpine
- 15-alpine
- 16-alpine
steps:
@ -252,8 +250,8 @@ jobs:
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-server:buildcache
cache-to: type=registry,ref=ghcr.io/goauthentik/dev-server:buildcache,mode=max
platforms: linux/${{ matrix.arch }}
pr-comment:
needs:

View File

@ -105,8 +105,8 @@ jobs:
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
platforms: linux/amd64,linux/arm64
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
cache-to: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache,mode=max
build-binary:
timeout-minutes: 120
needs:

15
.vscode/settings.json vendored
View File

@ -4,20 +4,21 @@
"asgi",
"authentik",
"authn",
"entra",
"goauthentik",
"jwks",
"kubernetes",
"oidc",
"openid",
"passwordless",
"plex",
"saml",
"totp",
"webauthn",
"traefik",
"passwordless",
"kubernetes",
"sso",
"slo",
"scim",
"slo",
"sso",
"totp",
"traefik",
"webauthn",
],
"todo-tree.tree.showCountsInTree": true,
"todo-tree.tree.showBadges": true,

View File

@ -38,7 +38,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
RUN npm run build
# Stage 3: Build go proxy
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.22.3-bookworm AS go-builder
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS go-builder
ARG TARGETOS
ARG TARGETARCH
@ -49,6 +49,11 @@ ARG GOARCH=$TARGETARCH
WORKDIR /go/src/goauthentik.io
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends crossbuild-essential-arm64 gcc-aarch64-linux-gnu
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
--mount=type=cache,target=/go/pkg/mod \
@ -63,11 +68,11 @@ COPY ./internal /go/src/goauthentik.io/internal
COPY ./go.mod /go/src/goauthentik.io/go.mod
COPY ./go.sum /go/src/goauthentik.io/go.sum
ENV CGO_ENABLED=0
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server
if [ "$TARGETARCH" = "arm64" ]; then export CC=aarch64-linux-gnu-gcc && export CC_FOR_TARGET=gcc-aarch64-linux-gnu; fi && \
CGO_ENABLED=1 GOEXPERIMENT="systemcrypto" GOFLAGS="-tags=requirefips" GOARM="${TARGETVARIANT#v}" \
go build -o /go/authentik ./cmd/server
# Stage 4: MaxMind GeoIP
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 as geoip
@ -84,7 +89,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 5: Python dependencies
FROM docker.io/python:3.12.3-slim-bookworm AS python-deps
FROM ghcr.io/goauthentik/fips-python:3.12.3-slim-bookworm-fips-full AS python-deps
WORKDIR /ak-root/poetry
@ -97,7 +102,7 @@ RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloa
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
apt-get update && \
# Required for installing pip packages
apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev libpq-dev
apt-get install -y --no-install-recommends build-essential pkg-config libpq-dev
RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
--mount=type=bind,target=./poetry.lock,src=./poetry.lock \
@ -105,12 +110,13 @@ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
--mount=type=cache,target=/root/.cache/pypoetry \
python -m venv /ak-root/venv/ && \
bash -c "source ${VENV_PATH}/bin/activate && \
pip3 install --upgrade pip && \
pip3 install poetry && \
poetry install --only=main --no-ansi --no-interaction --no-root"
pip3 install --upgrade pip && \
pip3 install poetry && \
poetry install --only=main --no-ansi --no-interaction --no-root && \
pip install --force-reinstall /wheels/*"
# Stage 6: Run
FROM docker.io/python:3.12.3-slim-bookworm AS final-image
FROM ghcr.io/goauthentik/fips-python:3.12.3-slim-bookworm-fips-full AS final-image
ARG GIT_BUILD_HASH
ARG VERSION
@ -127,7 +133,7 @@ WORKDIR /
# We cannot cache this layer otherwise we'll end up with a bigger image
RUN apt-get update && \
# Required for runtime
apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 ca-certificates && \
apt-get install -y --no-install-recommends libpq5 libmaxminddb0 ca-certificates && \
# Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \
apt-get clean && \
@ -163,6 +169,8 @@ ENV TMPDIR=/dev/shm/ \
VENV_PATH="/ak-root/venv" \
POETRY_VIRTUALENVS_CREATE=false
ENV GOFIPS=1
HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ]
ENTRYPOINT [ "dumb-init", "--", "ak" ]

View File

@ -253,6 +253,7 @@ website-watch: ## Build and watch the documentation website, updating automatic
#########################
docker: ## Build a docker image of the current source tree
mkdir -p ${GEN_API_TS}
DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
#########################

View File

@ -2,17 +2,19 @@
import platform
from datetime import datetime
from ssl import OPENSSL_VERSION
from sys import version as python_version
from typing import TypedDict
from cryptography.hazmat.backends.openssl.backend import backend
from django.utils.timezone import now
from drf_spectacular.utils import extend_schema
from gunicorn import version_info as gunicorn_version
from rest_framework.fields import SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from authentik import get_full_version
from authentik.core.api.utils import PassiveSerializer
from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_env
@ -25,11 +27,13 @@ class RuntimeDict(TypedDict):
"""Runtime information"""
python_version: str
gunicorn_version: str
environment: str
architecture: str
platform: str
uname: str
openssl_version: str
openssl_fips_mode: bool
authentik_version: str
class SystemInfoSerializer(PassiveSerializer):
@ -64,11 +68,13 @@ class SystemInfoSerializer(PassiveSerializer):
def get_runtime(self, request: Request) -> RuntimeDict:
"""Get versions"""
return {
"python_version": python_version,
"gunicorn_version": ".".join(str(x) for x in gunicorn_version),
"environment": get_env(),
"architecture": platform.machine(),
"authentik_version": get_full_version(),
"environment": get_env(),
"openssl_fips_enabled": backend._fips_enabled,
"openssl_version": OPENSSL_VERSION,
"platform": platform.platform(),
"python_version": python_version,
"uname": " ".join(platform.uname()),
}

View File

@ -75,7 +75,7 @@ class BlueprintEntry:
_state: BlueprintEntryState = field(default_factory=BlueprintEntryState)
def __post_init__(self, *args, **kwargs) -> None:
self.__tag_contexts: list["YAMLTagContext"] = []
self.__tag_contexts: list[YAMLTagContext] = []
@staticmethod
def from_model(model: SerializerModel, *extra_identifier_names: str) -> "BlueprintEntry":

View File

@ -39,6 +39,14 @@ from authentik.core.models import (
)
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import LicenseUsage
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceProviderGroup,
GoogleWorkspaceProviderUser,
)
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProviderGroup,
MicrosoftEntraProviderUser,
)
from authentik.enterprise.providers.rac.models import ConnectionToken
from authentik.events.logs import LogEvent, capture_logs
from authentik.events.models import SystemTask
@ -50,7 +58,7 @@ from authentik.outposts.models import OutpostServiceConnection
from authentik.policies.models import Policy, PolicyBindingModel
from authentik.policies.reputation.models import Reputation
from authentik.providers.oauth2.models import AccessToken, AuthorizationCode, RefreshToken
from authentik.providers.scim.models import SCIMGroup, SCIMUser
from authentik.providers.scim.models import SCIMProviderGroup, SCIMProviderUser
from authentik.sources.scim.models import SCIMSourceGroup, SCIMSourceUser
from authentik.stages.authenticator_webauthn.models import WebAuthnDeviceType
from authentik.tenants.models import Tenant
@ -86,10 +94,11 @@ def excluded_models() -> list[type[Model]]:
# Classes that have other dependencies
AuthenticatedSession,
# Classes which are only internally managed
# FIXME: these shouldn't need to be explicitly listed, but rather based off of a mixin
FlowToken,
LicenseUsage,
SCIMGroup,
SCIMUser,
SCIMProviderGroup,
SCIMProviderUser,
Tenant,
SystemTask,
ConnectionToken,
@ -100,6 +109,10 @@ def excluded_models() -> list[type[Model]]:
WebAuthnDeviceType,
SCIMSourceUser,
SCIMSourceGroup,
GoogleWorkspaceProviderUser,
GoogleWorkspaceProviderGroup,
MicrosoftEntraProviderUser,
MicrosoftEntraProviderGroup,
)

View File

@ -2,6 +2,7 @@
from json import loads
from django.db.models import Prefetch
from django.http import Http404
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
from django_filters.filterset import FilterSet
@ -166,8 +167,14 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
def get_queryset(self):
base_qs = Group.objects.all().select_related("parent").prefetch_related("roles")
if self.serializer_class(context={"request": self.request})._should_include_users:
base_qs = base_qs.prefetch_related("users")
else:
base_qs = base_qs.prefetch_related(
Prefetch("users", queryset=User.objects.all().only("id"))
)
return base_qs
@extend_schema(
@ -178,6 +185,14 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@extend_schema(
parameters=[
OpenApiParameter("include_users", bool, default=True),
]
)
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@permission_required("authentik_core.add_user_to_group")
@extend_schema(
request=UserAccountSerializer,

View File

@ -0,0 +1,79 @@
"""API Utilities"""
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.fields import (
BooleanField,
CharField,
)
from rest_framework.request import Request
from rest_framework.response import Response
from authentik.core.api.utils import PassiveSerializer
from authentik.enterprise.apps import EnterpriseConfig
from authentik.lib.utils.reflection import all_subclasses
class TypeCreateSerializer(PassiveSerializer):
"""Types of an object that can be created"""
name = CharField(required=True)
description = CharField(required=True)
component = CharField(required=True)
model_name = CharField(required=True)
icon_url = CharField(required=False)
requires_enterprise = BooleanField(default=False)
class CreatableType:
"""Class to inherit from to mark a model as creatable, even if the model itself is marked
as abstract"""
class NonCreatableType:
"""Class to inherit from to mark a model as non-creatable even if it is not abstract"""
class TypesMixin:
"""Mixin which adds an API endpoint to list all possible types that can be created"""
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request, additional: list[dict] | None = None) -> Response:
"""Get all creatable types"""
data = []
for subclass in all_subclasses(self.queryset.model):
instance = None
if subclass._meta.abstract:
if not issubclass(subclass, CreatableType):
continue
# Circumvent the django protection for not being able to instantiate
# abstract models. We need a model instance to access .component
# and further down .icon_url
instance = subclass.__new__(subclass)
# Django re-sets abstract = False so we need to override that
instance.Meta.abstract = True
else:
if issubclass(subclass, NonCreatableType):
continue
instance = subclass()
try:
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": instance.component,
"model_name": subclass._meta.model_name,
"icon_url": getattr(instance, "icon_url", None),
"requires_enterprise": isinstance(
subclass._meta.app_config, EnterpriseConfig
),
}
)
except NotImplementedError:
continue
if additional:
data.extend(additional)
data = sorted(data, key=lambda x: x["name"])
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -9,18 +9,22 @@ from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.fields import BooleanField, CharField
from rest_framework.relations import PrimaryKeyRelatedField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import GenericViewSet
from authentik.blueprints.api import ManagedSerializer
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
from authentik.core.api.utils import (
MetaNameSerializer,
PassiveSerializer,
)
from authentik.core.expression.evaluator import PropertyMappingEvaluator
from authentik.core.models import PropertyMapping
from authentik.core.models import Group, PropertyMapping, User
from authentik.events.utils import sanitize_item
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.api.exec import PolicyTestSerializer
from authentik.rbac.decorators import permission_required
@ -64,6 +68,7 @@ class PropertyMappingSerializer(ManagedSerializer, ModelSerializer, MetaNameSeri
class PropertyMappingViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -72,7 +77,15 @@ class PropertyMappingViewSet(
):
"""PropertyMapping Viewset"""
queryset = PropertyMapping.objects.none()
class PropertyMappingTestSerializer(PolicyTestSerializer):
"""Test property mapping execution for a user/group with context"""
user = PrimaryKeyRelatedField(queryset=User.objects.all(), required=False, allow_null=True)
group = PrimaryKeyRelatedField(
queryset=Group.objects.all(), required=False, allow_null=True
)
queryset = PropertyMapping.objects.select_subclasses()
serializer_class = PropertyMappingSerializer
search_fields = [
"name",
@ -80,29 +93,9 @@ class PropertyMappingViewSet(
filterset_fields = {"managed": ["isnull"]}
ordering = ["name"]
def get_queryset(self): # pragma: no cover
return PropertyMapping.objects.select_subclasses()
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable property-mapping types"""
data = []
for subclass in all_subclasses(self.queryset.model):
subclass: PropertyMapping
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@permission_required("authentik_core.view_propertymapping")
@extend_schema(
request=PolicyTestSerializer(),
request=PropertyMappingTestSerializer(),
responses={
200: PropertyMappingTestResultSerializer,
400: OpenApiResponse(description="Invalid parameters"),
@ -120,29 +113,39 @@ class PropertyMappingViewSet(
"""Test Property Mapping"""
_mapping: PropertyMapping = self.get_object()
# Use `get_subclass` to get correct class and correct `.evaluate` implementation
mapping = PropertyMapping.objects.get_subclass(pk=_mapping.pk)
mapping: PropertyMapping = PropertyMapping.objects.get_subclass(pk=_mapping.pk)
# FIXME: when we separate policy mappings between ones for sources
# and ones for providers, we need to make the user field optional for the source mapping
test_params = PolicyTestSerializer(data=request.data)
test_params = self.PropertyMappingTestSerializer(data=request.data)
if not test_params.is_valid():
return Response(test_params.errors, status=400)
format_result = str(request.GET.get("format_result", "false")).lower() == "true"
# User permission check, only allow mapping testing for users that are readable
users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
pk=test_params.validated_data["user"].pk
)
if not users.exists():
raise PermissionDenied()
context: dict = test_params.validated_data.get("context", {})
context.setdefault("user", None)
if user := test_params.validated_data.get("user"):
# User permission check, only allow mapping testing for users that are readable
users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
pk=user.pk
)
if not users.exists():
raise PermissionDenied()
context["user"] = user
if group := test_params.validated_data.get("group"):
# Group permission check, only allow mapping testing for groups that are readable
groups = get_objects_for_user(request.user, "authentik_core.view_group").filter(
pk=group.pk
)
if not groups.exists():
raise PermissionDenied()
context["group"] = group
context["request"] = self.request
response_data = {"successful": True, "result": ""}
try:
result = mapping.evaluate(
users.first(),
self.request,
**test_params.validated_data.get("context", {}),
)
result = mapping.evaluate(**context)
response_data["result"] = dumps(
sanitize_item(result), indent=(4 if format_result else None)
)

View File

@ -5,20 +5,15 @@ from django.db.models.query import Q
from django.utils.translation import gettext_lazy as _
from django_filters.filters import BooleanFilter
from django_filters.filterset import FilterSet
from drf_spectacular.utils import extend_schema
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.fields import ReadOnlyField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.models import Provider
from authentik.enterprise.apps import EnterpriseConfig
from authentik.lib.utils.reflection import all_subclasses
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
@ -63,8 +58,12 @@ class ProviderFilter(FilterSet):
"""Filter for providers"""
application__isnull = BooleanFilter(method="filter_application__isnull")
backchannel_only = BooleanFilter(
method="filter_backchannel_only",
backchannel = BooleanFilter(
method="filter_backchannel",
label=_(
"When not set all providers are returned. When set to true, only backchannel "
"providers are returned. When set to false, backchannel providers are excluded"
),
)
def filter_application__isnull(self, queryset: QuerySet, name, value):
@ -75,12 +74,14 @@ class ProviderFilter(FilterSet):
| Q(application__isnull=value)
)
def filter_backchannel_only(self, queryset: QuerySet, name, value):
"""Only return backchannel providers"""
def filter_backchannel(self, queryset: QuerySet, name, value):
"""By default all providers are returned. When set to true, only backchannel providers are
returned. When set to false, backchannel providers are excluded"""
return queryset.filter(is_backchannel=value)
class ProviderViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -99,31 +100,3 @@ class ProviderViewSet(
def get_queryset(self): # pragma: no cover
return Provider.objects.select_subclasses()
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable provider types"""
data = []
for subclass in all_subclasses(self.queryset.model):
subclass: Provider
if subclass._meta.abstract:
continue
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
}
)
data.append(
{
"name": _("SAML Provider from Metadata"),
"description": _("Create a SAML Provider by importing its Metadata."),
"component": "ak-provider-saml-import-form",
"model_name": "",
}
)
return Response(TypeCreateSerializer(data, many=True).data)

View File

@ -17,8 +17,9 @@ from structlog.stdlib import get_logger
from authentik.api.authorization import OwnerFilter, OwnerSuperuserPermissions
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.models import Source, UserSourceConnection
from authentik.core.types import UserSettingSerializer
from authentik.lib.utils.file import (
@ -27,7 +28,6 @@ from authentik.lib.utils.file import (
set_file,
set_file_url,
)
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.engine import PolicyEngine
from authentik.rbac.decorators import permission_required
@ -74,6 +74,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
class SourceViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -132,30 +133,6 @@ class SourceViewSet(
source: Source = self.get_object()
return set_file_url(request, source, "icon")
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable source types"""
data = []
for subclass in all_subclasses(self.queryset.model):
subclass: Source
component = ""
if len(subclass.__subclasses__()) > 0:
continue
if subclass._meta.abstract:
component = subclass.__bases__[0]().component
else:
component = subclass().component
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@extend_schema(responses={200: UserSettingSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def user_settings(self, request: Request) -> Response:

View File

@ -39,12 +39,12 @@ def get_delete_action(manager: Manager) -> str:
"""Get the delete action from the Foreign key, falls back to cascade"""
if hasattr(manager, "field"):
if manager.field.remote_field.on_delete.__name__ == SET_NULL.__name__:
return DeleteAction.SET_NULL.name
return DeleteAction.SET_NULL.value
if manager.field.remote_field.on_delete.__name__ == SET_DEFAULT.__name__:
return DeleteAction.SET_DEFAULT.name
return DeleteAction.SET_DEFAULT.value
if hasattr(manager, "source_field"):
return DeleteAction.CASCADE_MANY.name
return DeleteAction.CASCADE.name
return DeleteAction.CASCADE_MANY.value
return DeleteAction.CASCADE.value
class UsedByMixin:

View File

@ -6,8 +6,16 @@ from django.db.models import Model
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
from drf_spectacular.plumbing import build_basic_type
from drf_spectacular.types import OpenApiTypes
from rest_framework.fields import BooleanField, CharField, IntegerField, JSONField
from rest_framework.serializers import Serializer, SerializerMethodField, ValidationError
from rest_framework.fields import (
CharField,
IntegerField,
JSONField,
SerializerMethodField,
)
from rest_framework.serializers import (
Serializer,
ValidationError,
)
def is_dict(value: Any):
@ -68,16 +76,6 @@ class MetaNameSerializer(PassiveSerializer):
return f"{obj._meta.app_label}.{obj._meta.model_name}"
class TypeCreateSerializer(PassiveSerializer):
"""Types of an object that can be created"""
name = CharField(required=True)
description = CharField(required=True)
component = CharField(required=True)
model_name = CharField(required=True)
requires_enterprise = BooleanField(default=False)
class CacheSerializer(PassiveSerializer):
"""Generic cache stats for an object"""

View File

@ -31,8 +31,9 @@ class InbuiltBackend(ModelBackend):
# Since we can't directly pass other variables to signals, and we want to log the method
# and the token used, we assume we're running in a flow and set a variable in the context
flow_plan: FlowPlan = request.session.get(SESSION_KEY_PLAN, FlowPlan(""))
flow_plan.context[PLAN_CONTEXT_METHOD] = method
flow_plan.context[PLAN_CONTEXT_METHOD_ARGS] = cleanse_dict(sanitize_dict(kwargs))
flow_plan.context.setdefault(PLAN_CONTEXT_METHOD, method)
flow_plan.context.setdefault(PLAN_CONTEXT_METHOD_ARGS, {})
flow_plan.context[PLAN_CONTEXT_METHOD_ARGS].update(cleanse_dict(sanitize_dict(kwargs)))
request.session[SESSION_KEY_PLAN] = flow_plan

View File

@ -1,5 +1,6 @@
"""Property Mapping Evaluator"""
from types import CodeType
from typing import Any
from django.db.models import Model
@ -24,6 +25,8 @@ class PropertyMappingEvaluator(BaseEvaluator):
"""Custom Evaluator that adds some different context variables."""
dry_run: bool
model: Model
_compiled: CodeType | None = None
def __init__(
self,
@ -33,23 +36,32 @@ class PropertyMappingEvaluator(BaseEvaluator):
dry_run: bool | None = False,
**kwargs,
):
self.model = model
if hasattr(model, "name"):
_filename = model.name
else:
_filename = str(model)
super().__init__(filename=_filename)
self.dry_run = dry_run
self.set_context(user, request, **kwargs)
def set_context(
self,
user: User | None = None,
request: HttpRequest | None = None,
**kwargs,
):
req = PolicyRequest(user=User())
req.obj = model
req.obj = self.model
if user:
req.user = user
self._context["user"] = user
if request:
req.http_request = request
self._context["request"] = req
req.context.update(**kwargs)
self._context["request"] = req
self._context.update(**kwargs)
self._globals["SkipObject"] = SkipObjectException
self.dry_run = dry_run
def handle_error(self, exc: Exception, expression_source: str):
"""Exception Handler"""
@ -71,3 +83,9 @@ class PropertyMappingEvaluator(BaseEvaluator):
def evaluate(self, *args, **kwargs) -> Any:
with PROPERTY_MAPPING_TIME.labels(mapping_name=self._filename).time():
return super().evaluate(*args, **kwargs)
def compile(self, expression: str | None = None) -> Any:
if not self._compiled:
compiled = super().compile(expression or self.model.expression)
self._compiled = compiled
return self._compiled

View File

@ -6,6 +6,11 @@ from authentik.lib.sentry import SentryIgnoredException
class PropertyMappingExpressionException(SentryIgnoredException):
"""Error when a PropertyMapping Exception expression could not be parsed or evaluated."""
def __init__(self, exc: Exception, mapping) -> None:
super().__init__()
self.exc = exc
self.mapping = mapping
class SkipObjectException(PropertyMappingExpressionException):
"""Exception which can be raised in a property mapping to skip syncing an object.

View File

@ -15,6 +15,7 @@ from django.http import HttpRequest
from django.utils.functional import SimpleLazyObject, cached_property
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django_cte import CTEQuerySet, With
from guardian.conf import settings
from guardian.mixins import GuardianUserMixin
from model_utils.managers import InheritanceManager
@ -56,6 +57,8 @@ options.DEFAULT_NAMES = options.DEFAULT_NAMES + (
"authentik_used_by_shadows",
)
GROUP_RECURSION_LIMIT = 20
def default_token_duration() -> datetime:
"""Default duration a Token is valid"""
@ -96,6 +99,40 @@ class UserTypes(models.TextChoices):
INTERNAL_SERVICE_ACCOUNT = "internal_service_account"
class GroupQuerySet(CTEQuerySet):
def with_children_recursive(self):
"""Recursively get all groups that have the current queryset as parents
or are indirectly related."""
def make_cte(cte):
"""Build the query that ends up in WITH RECURSIVE"""
# Start from self, aka the current query
# Add a depth attribute to limit the recursion
return self.annotate(
relative_depth=models.Value(0, output_field=models.IntegerField())
).union(
# Here is the recursive part of the query. cte refers to the previous iteration
# Only select groups for which the parent is part of the previous iteration
# and increase the depth
# Finally, limit the depth
cte.join(Group, group_uuid=cte.col.parent_id)
.annotate(
relative_depth=models.ExpressionWrapper(
cte.col.relative_depth
+ models.Value(1, output_field=models.IntegerField()),
output_field=models.IntegerField(),
)
)
.filter(relative_depth__lt=GROUP_RECURSION_LIMIT),
all=True,
)
# Build the recursive query, see above
cte = With.recursive(make_cte)
# Return the result, as a usable queryset for Group.
return cte.join(Group, group_uuid=cte.col.group_uuid).with_cte(cte)
class Group(SerializerModel):
"""Group model which supports a basic hierarchy and has attributes"""
@ -118,6 +155,8 @@ class Group(SerializerModel):
)
attributes = models.JSONField(default=dict, blank=True)
objects = GroupQuerySet.as_manager()
@property
def serializer(self) -> Serializer:
from authentik.core.api.groups import GroupSerializer
@ -136,36 +175,11 @@ class Group(SerializerModel):
return user.all_groups().filter(group_uuid=self.group_uuid).exists()
def children_recursive(self: Self | QuerySet["Group"]) -> QuerySet["Group"]:
"""Recursively get all groups that have this as parent or are indirectly related"""
direct_groups = []
if isinstance(self, QuerySet):
direct_groups = list(x for x in self.all().values_list("pk", flat=True).iterator())
else:
direct_groups = [self.pk]
if len(direct_groups) < 1:
return Group.objects.none()
query = """
WITH RECURSIVE parents AS (
SELECT authentik_core_group.*, 0 AS relative_depth
FROM authentik_core_group
WHERE authentik_core_group.group_uuid = ANY(%s)
UNION ALL
SELECT authentik_core_group.*, parents.relative_depth + 1
FROM authentik_core_group, parents
WHERE (
authentik_core_group.group_uuid = parents.parent_id and
parents.relative_depth < 20
)
)
SELECT group_uuid
FROM parents
GROUP BY group_uuid, name
ORDER BY name;
"""
group_pks = [group.pk for group in Group.objects.raw(query, [direct_groups]).iterator()]
return Group.objects.filter(pk__in=group_pks)
"""Compatibility layer for Group.objects.with_children_recursive()"""
qs = self
if not isinstance(self, QuerySet):
qs = Group.objects.filter(group_uuid=self.group_uuid)
return qs.with_children_recursive()
def __str__(self):
return f"Group {self.name}"
@ -232,10 +246,8 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
return User._meta.get_field("path").default
def all_groups(self) -> QuerySet[Group]:
"""Recursively get all groups this user is a member of.
At least one query is done to get the direct groups of the user, with groups
there are at most 3 queries done"""
return Group.children_recursive(self.ak_groups.all())
"""Recursively get all groups this user is a member of."""
return self.ak_groups.all().with_children_recursive()
def group_attributes(self, request: HttpRequest | None = None) -> dict[str, Any]:
"""Get a dictionary containing the attributes from all groups the user belongs to,
@ -377,6 +389,10 @@ class Provider(SerializerModel):
Can return None for providers that are not URL-based"""
return None
@property
def icon_url(self) -> str | None:
return None
@property
def component(self) -> str:
"""Return component used to edit this object"""
@ -768,7 +784,7 @@ class PropertyMapping(SerializerModel, ManagedModel):
try:
return evaluator.evaluate(self.expression)
except Exception as exc:
raise PropertyMappingExpressionException(exc) from exc
raise PropertyMappingExpressionException(self, exc) from exc
def __str__(self):
return f"Property Mapping {self.name}"

View File

@ -13,7 +13,7 @@ from django.utils.translation import gettext as _
from structlog.stdlib import get_logger
from authentik.core.models import Source, SourceUserMatchingModes, User, UserSourceConnection
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostUserEnrollmentStage
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION, PostSourceStage
from authentik.events.models import Event, EventAction
from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import Flow, FlowToken, Stage, in_memory_stage
@ -206,13 +206,9 @@ class SourceFlowManager:
def get_stages_to_append(self, flow: Flow) -> list[Stage]:
"""Hook to override stages which are appended to the flow"""
if not self.source.enrollment_flow:
return []
if flow.slug == self.source.enrollment_flow.slug:
return [
in_memory_stage(PostUserEnrollmentStage),
]
return []
return [
in_memory_stage(PostSourceStage),
]
def _prepare_flow(
self,
@ -266,6 +262,9 @@ class SourceFlowManager:
)
# We run the Flow planner here so we can pass the Pending user in the context
planner = FlowPlanner(flow)
# We append some stages so the initial flow we get might be empty
planner.allow_empty_flows = True
planner.use_cache = False
plan = planner.plan(self.request, kwargs)
for stage in self.get_stages_to_append(flow):
plan.append_stage(stage)
@ -324,7 +323,7 @@ class SourceFlowManager:
reverse(
"authentik_core:if-user",
)
+ f"#/settings;page-{self.source.slug}"
+ "#/settings;page-sources"
)
def handle_enroll(

View File

@ -10,7 +10,7 @@ from authentik.flows.stage import StageView
PLAN_CONTEXT_SOURCES_CONNECTION = "goauthentik.io/sources/connection"
class PostUserEnrollmentStage(StageView):
class PostSourceStage(StageView):
"""Dynamically injected stage which saves the Connection after
the user has been enrolled."""
@ -21,10 +21,12 @@ class PostUserEnrollmentStage(StageView):
]
user: User = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
connection.user = user
linked = connection.pk is None
connection.save()
Event.new(
EventAction.SOURCE_LINKED,
message="Linked Source",
source=connection.source,
).from_http(self.request)
if linked:
Event.new(
EventAction.SOURCE_LINKED,
message="Linked Source",
source=connection.source,
).from_http(self.request)
return self.executor.stage_ok()

View File

@ -23,6 +23,17 @@ class TestGroupsAPI(APITestCase):
response = self.client.get(reverse("authentik_api:group-list"), {"include_users": "true"})
self.assertEqual(response.status_code, 200)
def test_retrieve_with_users(self):
"""Test retrieve with users"""
admin = create_test_admin_user()
group = Group.objects.create(name=generate_id())
self.client.force_login(admin)
response = self.client.get(
reverse("authentik_api:group-detail", kwargs={"pk": group.pk}),
{"include_users": "true"},
)
self.assertEqual(response.status_code, 200)
def test_add_user(self):
"""Test add_user"""
group = Group.objects.create(name=generate_id())

View File

@ -1,14 +1,14 @@
"""authentik core models tests"""
from collections.abc import Callable
from time import sleep
from datetime import timedelta
from django.test import RequestFactory, TestCase
from django.utils.timezone import now
from freezegun import freeze_time
from guardian.shortcuts import get_anonymous_user
from authentik.core.models import Provider, Source, Token
from authentik.flows.models import Stage
from authentik.lib.utils.reflection import all_subclasses
@ -17,18 +17,20 @@ class TestModels(TestCase):
def test_token_expire(self):
"""Test token expiring"""
token = Token.objects.create(expires=now(), user=get_anonymous_user())
sleep(0.5)
self.assertTrue(token.is_expired)
with freeze_time() as freeze:
token = Token.objects.create(expires=now(), user=get_anonymous_user())
freeze.tick(timedelta(seconds=1))
self.assertTrue(token.is_expired)
def test_token_expire_no_expire(self):
"""Test token expiring with "expiring" set"""
token = Token.objects.create(expires=now(), user=get_anonymous_user(), expiring=False)
sleep(0.5)
self.assertFalse(token.is_expired)
with freeze_time() as freeze:
token = Token.objects.create(expires=now(), user=get_anonymous_user(), expiring=False)
freeze.tick(timedelta(seconds=1))
self.assertFalse(token.is_expired)
def source_tester_factory(test_model: type[Stage]) -> Callable:
def source_tester_factory(test_model: type[Source]) -> Callable:
"""Test source"""
factory = RequestFactory()
@ -36,19 +38,19 @@ def source_tester_factory(test_model: type[Stage]) -> Callable:
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract: # pragma: no cover
model_class = test_model.__bases__[0]()
if test_model._meta.abstract:
model_class = [x for x in test_model.__bases__ if issubclass(x, Source)][0]()
else:
model_class = test_model()
model_class.slug = "test"
self.assertIsNotNone(model_class.component)
_ = model_class.ui_login_button(request)
_ = model_class.ui_user_settings()
model_class.ui_login_button(request)
model_class.ui_user_settings()
return tester
def provider_tester_factory(test_model: type[Stage]) -> Callable:
def provider_tester_factory(test_model: type[Provider]) -> Callable:
"""Test provider"""
def tester(self: TestModels):

View File

@ -6,9 +6,10 @@ from django.urls import reverse
from rest_framework.serializers import ValidationError
from rest_framework.test import APITestCase
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.models import PropertyMapping
from authentik.core.api.property_mappings import PropertyMappingSerializer
from authentik.core.models import Group, PropertyMapping
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
class TestPropertyMappingAPI(APITestCase):
@ -16,23 +17,40 @@ class TestPropertyMappingAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar'}"""
)
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_test_call(self):
"""Test PropertMappings's test endpoint"""
"""Test PropertyMappings's test endpoint"""
mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar', 'baz': user.username}"""
)
response = self.client.post(
reverse("authentik_api:propertymapping-test", kwargs={"pk": self.mapping.pk}),
reverse("authentik_api:propertymapping-test", kwargs={"pk": mapping.pk}),
data={
"user": self.user.pk,
},
)
self.assertJSONEqual(
response.content.decode(),
{"result": dumps({"foo": "bar"}), "successful": True},
{"result": dumps({"foo": "bar", "baz": self.user.username}), "successful": True},
)
def test_test_call_group(self):
"""Test PropertyMappings's test endpoint"""
mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar', 'baz': group.name}"""
)
group = Group.objects.create(name=generate_id())
response = self.client.post(
reverse("authentik_api:propertymapping-test", kwargs={"pk": mapping.pk}),
data={
"group": group.pk,
},
)
self.assertJSONEqual(
response.content.decode(),
{"result": dumps({"foo": "bar", "baz": group.name}), "successful": True},
)
def test_validate(self):

View File

@ -2,11 +2,15 @@
from django.contrib.auth.models import AnonymousUser
from django.test import TestCase
from django.urls import reverse
from guardian.utils import get_anonymous_user
from authentik.core.models import SourceUserMatchingModes, User
from authentik.core.sources.flow_manager import Action
from authentik.core.sources.stage import PostSourceStage
from authentik.core.tests.utils import create_test_flow
from authentik.flows.planner import FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.policies.denied import AccessDeniedResponse
@ -21,41 +25,55 @@ class TestSourceFlowManager(TestCase):
def setUp(self) -> None:
super().setUp()
self.source: OAuthSource = OAuthSource.objects.create(name="test")
self.authentication_flow = create_test_flow()
self.enrollment_flow = create_test_flow()
self.source: OAuthSource = OAuthSource.objects.create(
name=generate_id(),
slug=generate_id(),
authentication_flow=self.authentication_flow,
enrollment_flow=self.enrollment_flow,
)
self.identifier = generate_id()
def test_unauthenticated_enroll(self):
"""Test un-authenticated user enrolling"""
flow_manager = OAuthSourceFlowManager(
self.source, get_request("/", user=AnonymousUser()), self.identifier, {}
)
request = get_request("/", user=AnonymousUser())
flow_manager = OAuthSourceFlowManager(self.source, request, self.identifier, {})
action, _ = flow_manager.get_action()
self.assertEqual(action, Action.ENROLL)
flow_manager.get_flow()
response = flow_manager.get_flow()
self.assertEqual(response.status_code, 302)
flow_plan: FlowPlan = request.session[SESSION_KEY_PLAN]
self.assertEqual(flow_plan.bindings[0].stage.view, PostSourceStage)
def test_unauthenticated_auth(self):
"""Test un-authenticated user authenticating"""
UserOAuthSourceConnection.objects.create(
user=get_anonymous_user(), source=self.source, identifier=self.identifier
)
flow_manager = OAuthSourceFlowManager(
self.source, get_request("/", user=AnonymousUser()), self.identifier, {}
)
request = get_request("/", user=AnonymousUser())
flow_manager = OAuthSourceFlowManager(self.source, request, self.identifier, {})
action, _ = flow_manager.get_action()
self.assertEqual(action, Action.AUTH)
flow_manager.get_flow()
response = flow_manager.get_flow()
self.assertEqual(response.status_code, 302)
flow_plan: FlowPlan = request.session[SESSION_KEY_PLAN]
self.assertEqual(flow_plan.bindings[0].stage.view, PostSourceStage)
def test_authenticated_link(self):
"""Test authenticated user linking"""
user = User.objects.create(username="foo", email="foo@bar.baz")
flow_manager = OAuthSourceFlowManager(
self.source, get_request("/", user=user), self.identifier, {}
)
request = get_request("/", user=user)
flow_manager = OAuthSourceFlowManager(self.source, request, self.identifier, {})
action, connection = flow_manager.get_action()
self.assertEqual(action, Action.LINK)
self.assertIsNone(connection.pk)
flow_manager.get_flow()
response = flow_manager.get_flow()
self.assertEqual(response.status_code, 302)
self.assertEqual(
response.url,
reverse("authentik_core:if-user") + "#/settings;page-sources",
)
def test_unauthenticated_link(self):
"""Test un-authenticated user linking"""

View File

@ -42,8 +42,8 @@ class TestUsersAvatars(APITestCase):
with Mocker() as mocker:
mocker.head(
(
"https://secure.gravatar.com/avatar/84730f9c1851d1ea03f1a"
"a9ed85bd1ea?size=158&rating=g&default=404"
"https://www.gravatar.com/avatar/76eb3c74c8beb6faa037f1b6e2ecb3e252bdac"
"6cf71fb567ae36025a9d4ea86b?size=158&rating=g&default=404"
),
text="foo",
)

View File

@ -12,7 +12,7 @@ from authentik.core.api.applications import ApplicationViewSet
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
from authentik.core.api.devices import AdminDeviceViewSet, DeviceViewSet
from authentik.core.api.groups import GroupViewSet
from authentik.core.api.propertymappings import PropertyMappingViewSet
from authentik.core.api.property_mappings import PropertyMappingViewSet
from authentik.core.api.providers import ProviderViewSet
from authentik.core.api.sources import SourceViewSet, UserSourceConnectionViewSet
from authentik.core.api.tokens import TokenViewSet

View File

@ -92,7 +92,11 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
@property
def kid(self):
"""Get Key ID used for JWKS"""
return md5(self.key_data.encode("utf-8")).hexdigest() if self.key_data else "" # nosec
return (
md5(self.key_data.encode("utf-8"), usedforsecurity=False).hexdigest()
if self.key_data
else ""
) # nosec
def __str__(self) -> str:
return f"Certificate-Key Pair {self.name}"

View File

@ -241,7 +241,7 @@ class TestCrypto(APITestCase):
"model_name": "oauth2provider",
"pk": str(provider.pk),
"name": str(provider),
"action": DeleteAction.SET_NULL.name,
"action": DeleteAction.SET_NULL.value,
}
],
)

View File

@ -0,0 +1,45 @@
"""GoogleWorkspaceProviderGroup API Views"""
from rest_framework import mixins
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserGroupSerializer
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderGroup
class GoogleWorkspaceProviderGroupSerializer(ModelSerializer):
"""GoogleWorkspaceProviderGroup Serializer"""
group_obj = UserGroupSerializer(source="group", read_only=True)
class Meta:
model = GoogleWorkspaceProviderGroup
fields = [
"id",
"google_id",
"group",
"group_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class GoogleWorkspaceProviderGroupViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""GoogleWorkspaceProviderGroup Viewset"""
queryset = GoogleWorkspaceProviderGroup.objects.all().select_related("group")
serializer_class = GoogleWorkspaceProviderGroupSerializer
filterset_fields = ["provider__id", "group__name", "group__group_uuid"]
search_fields = ["provider__name", "group__name"]
ordering = ["group__name"]

View File

@ -6,21 +6,21 @@ from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.api.property_mappings import PropertyMappingSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderMapping
class GoogleProviderMappingSerializer(PropertyMappingSerializer):
"""GoogleProviderMapping Serializer"""
class GoogleWorkspaceProviderMappingSerializer(PropertyMappingSerializer):
"""GoogleWorkspaceProviderMapping Serializer"""
class Meta:
model = GoogleWorkspaceProviderMapping
fields = PropertyMappingSerializer.Meta.fields
class GoogleProviderMappingFilter(FilterSet):
"""Filter for GoogleProviderMapping"""
class GoogleWorkspaceProviderMappingFilter(FilterSet):
"""Filter for GoogleWorkspaceProviderMapping"""
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
@ -29,11 +29,11 @@ class GoogleProviderMappingFilter(FilterSet):
fields = "__all__"
class GoogleProviderMappingViewSet(UsedByMixin, ModelViewSet):
"""GoogleProviderMapping Viewset"""
class GoogleWorkspaceProviderMappingViewSet(UsedByMixin, ModelViewSet):
"""GoogleWorkspaceProviderMapping Viewset"""
queryset = GoogleWorkspaceProviderMapping.objects.all()
serializer_class = GoogleProviderMappingSerializer
filterset_class = GoogleProviderMappingFilter
serializer_class = GoogleWorkspaceProviderMappingSerializer
filterset_class = GoogleWorkspaceProviderMappingFilter
search_fields = ["name"]
ordering = ["name"]

View File

@ -10,8 +10,8 @@ from authentik.enterprise.providers.google_workspace.tasks import google_workspa
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
class GoogleProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
"""GoogleProvider Serializer"""
class GoogleWorkspaceProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
"""GoogleWorkspaceProvider Serializer"""
class Meta:
model = GoogleWorkspaceProvider
@ -38,11 +38,11 @@ class GoogleProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
extra_kwargs = {}
class GoogleProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelViewSet):
"""GoogleProvider Viewset"""
class GoogleWorkspaceProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelViewSet):
"""GoogleWorkspaceProvider Viewset"""
queryset = GoogleWorkspaceProvider.objects.all()
serializer_class = GoogleProviderSerializer
serializer_class = GoogleWorkspaceProviderSerializer
filterset_fields = [
"name",
"exclude_users_service_account",

View File

@ -0,0 +1,45 @@
"""GoogleWorkspaceProviderUser API Views"""
from rest_framework import mixins
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProviderUser
class GoogleWorkspaceProviderUserSerializer(ModelSerializer):
"""GoogleWorkspaceProviderUser Serializer"""
user_obj = GroupMemberSerializer(source="user", read_only=True)
class Meta:
model = GoogleWorkspaceProviderUser
fields = [
"id",
"google_id",
"user",
"user_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class GoogleWorkspaceProviderUserViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""GoogleWorkspaceProviderUser Viewset"""
queryset = GoogleWorkspaceProviderUser.objects.all().select_related("user")
serializer_class = GoogleWorkspaceProviderUserSerializer
filterset_fields = ["provider__id", "user__username", "user__id"]
search_fields = ["provider__name", "user__username"]
ordering = ["user__username"]

View File

@ -1,5 +1,5 @@
from django.db.models import Model
from django.http import HttpResponseNotFound
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from google.auth.exceptions import GoogleAuthError, TransportError
from googleapiclient.discovery import build
from googleapiclient.errors import Error, HttpError
@ -10,6 +10,7 @@ from authentik.enterprise.providers.google_workspace.models import GoogleWorkspa
from authentik.lib.sync.outgoing import HTTP_CONFLICT
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.exceptions import (
BadRequestSyncException,
NotFoundSyncException,
ObjectExistsSyncException,
StopSync,
@ -50,22 +51,24 @@ class GoogleWorkspaceSyncClient[TModel: Model, TConnection: Model, TSchema: dict
raise StopSync(exc) from exc
except HttpLib2Error as exc:
if isinstance(exc, HttpLib2ErrorWithResponse):
self._response_handle_status_code(exc.response.status, exc)
self._response_handle_status_code(request.body, exc.response.status, exc)
raise TransientSyncException(f"Failed to send request: {str(exc)}") from exc
except HttpError as exc:
self._response_handle_status_code(exc.status_code, exc)
self._response_handle_status_code(request.body, exc.status_code, exc)
raise TransientSyncException(f"Failed to send request: {str(exc)}") from exc
except Error as exc:
raise TransientSyncException(f"Failed to send request: {str(exc)}") from exc
return response
def _response_handle_status_code(self, status_code: int, root_exc: Exception):
def _response_handle_status_code(self, request: dict, status_code: int, root_exc: Exception):
if status_code == HttpResponseNotFound.status_code:
raise NotFoundSyncException("Object not found") from root_exc
if status_code == HTTP_CONFLICT:
raise ObjectExistsSyncException("Object exists") from root_exc
if status_code == HttpResponseBadRequest.status_code:
raise BadRequestSyncException("Bad request", request) from root_exc
def check_email_valid(self, *emails: str):
for email in emails:
if not any(email.endswith(f"@{domain_name}") for domain_name in self.domains):
raise TransientSyncException(f"Invalid email domain: {email}")
raise BadRequestSyncException(f"Invalid email domain: {email}")

View File

@ -1,28 +1,22 @@
from deepmerge import always_merger
from django.db import transaction
from django.utils.text import slugify
from authentik.core.expression.exceptions import (
PropertyMappingExpressionException,
SkipObjectException,
)
from authentik.core.models import Group
from authentik.enterprise.providers.google_workspace.clients.base import GoogleWorkspaceSyncClient
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceDeleteAction,
GoogleWorkspaceProvider,
GoogleWorkspaceProviderGroup,
GoogleWorkspaceProviderMapping,
GoogleWorkspaceProviderUser,
)
from authentik.events.models import Event, EventAction
from authentik.lib.sync.mapper import PropertyMappingManager
from authentik.lib.sync.outgoing.base import Direction
from authentik.lib.sync.outgoing.exceptions import (
NotFoundSyncException,
ObjectExistsSyncException,
StopSync,
TransientSyncException,
)
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
class GoogleWorkspaceGroupClient(
@ -34,41 +28,21 @@ class GoogleWorkspaceGroupClient(
connection_type_query = "group"
can_discover = True
def to_schema(self, obj: Group) -> dict:
"""Convert authentik group"""
raw_google_group = {
"email": f"{slugify(obj.name)}@{self.provider.default_group_email_domain}"
}
for mapping in (
self.provider.property_mappings_group.all().order_by("name").select_subclasses()
):
if not isinstance(mapping, GoogleWorkspaceProviderMapping):
continue
try:
mapping: GoogleWorkspaceProviderMapping
value = mapping.evaluate(
user=None,
request=None,
group=obj,
provider=self.provider,
)
if value is None:
continue
always_merger.merge(raw_google_group, value)
except SkipObjectException as exc:
raise exc from exc
except (PropertyMappingExpressionException, ValueError) as exc:
# Value error can be raised when assigning invalid data to an attribute
Event.new(
EventAction.CONFIGURATION_ERROR,
message=f"Failed to evaluate property-mapping {exception_to_string(exc)}",
mapping=mapping,
).save()
raise StopSync(exc, obj, mapping) from exc
if not raw_google_group:
raise StopSync(ValueError("No group mappings configured"), obj)
def __init__(self, provider: GoogleWorkspaceProvider) -> None:
super().__init__(provider)
self.mapper = PropertyMappingManager(
self.provider.property_mappings_group.all().order_by("name").select_subclasses(),
GoogleWorkspaceProviderMapping,
["group", "provider", "connection"],
)
return raw_google_group
def to_schema(self, obj: Group, connection: GoogleWorkspaceProviderGroup) -> dict:
"""Convert authentik group"""
return super().to_schema(
obj,
connection=connection,
email=f"{slugify(obj.name)}@{self.provider.default_group_email_domain}",
)
def delete(self, obj: Group):
"""Delete group"""
@ -79,7 +53,7 @@ class GoogleWorkspaceGroupClient(
self.logger.debug("Group does not exist in Google, skipping")
return None
with transaction.atomic():
if self.provider.group_delete_action == GoogleWorkspaceDeleteAction.DELETE:
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
self._request(
self.directory_service.groups().delete(groupKey=google_group.google_id)
)
@ -87,7 +61,7 @@ class GoogleWorkspaceGroupClient(
def create(self, group: Group):
"""Create group from scratch and create a connection object"""
google_group = self.to_schema(group)
google_group = self.to_schema(group, None)
self.check_email_valid(google_group["email"])
with transaction.atomic():
try:
@ -99,53 +73,49 @@ class GoogleWorkspaceGroupClient(
group_data = self._request(
self.directory_service.groups().get(groupKey=google_group["email"])
)
GoogleWorkspaceProviderGroup.objects.create(
provider=self.provider, group=group, google_id=group_data["id"]
return GoogleWorkspaceProviderGroup.objects.create(
provider=self.provider,
group=group,
google_id=group_data["id"],
attributes=group_data,
)
else:
GoogleWorkspaceProviderGroup.objects.create(
provider=self.provider, group=group, google_id=response["id"]
return GoogleWorkspaceProviderGroup.objects.create(
provider=self.provider,
group=group,
google_id=response["id"],
attributes=response,
)
def update(self, group: Group, connection: GoogleWorkspaceProviderGroup):
"""Update existing group"""
google_group = self.to_schema(group)
google_group = self.to_schema(group, connection)
self.check_email_valid(google_group["email"])
try:
return self._request(
response = self._request(
self.directory_service.groups().update(
groupKey=connection.google_id,
body=google_group,
)
)
connection.attributes = response
connection.save()
except NotFoundSyncException:
# Resource missing is handled by self.write, which will re-create the group
raise
def write(self, obj: Group):
google_group, created = super().write(obj)
if created:
self.create_sync_members(obj, google_group)
return google_group
self.create_sync_members(obj, google_group)
return google_group, created
def create_sync_members(self, obj: Group, google_group: dict):
def create_sync_members(self, obj: Group, google_group: GoogleWorkspaceProviderGroup):
"""Sync all members after a group was created"""
users = list(obj.users.order_by("id").values_list("id", flat=True))
connections = GoogleWorkspaceProviderUser.objects.filter(
provider=self.provider, user__pk__in=users
)
for user in connections:
try:
self._request(
self.directory_service.members().insert(
groupKey=google_group["id"],
body={
"email": user.google_id,
},
)
)
except TransientSyncException:
continue
).values_list("google_id", flat=True)
self._patch(google_group.google_id, Direction.add, connections)
def update_group(self, group: Group, action: Direction, users_set: set[int]):
"""Update a groups members"""
@ -242,4 +212,5 @@ class GoogleWorkspaceGroupClient(
provider=self.provider,
group=matching_authentik_group,
google_id=google_id,
attributes=group,
)

View File

@ -1,24 +1,18 @@
from deepmerge import always_merger
from django.db import transaction
from authentik.core.expression.exceptions import (
PropertyMappingExpressionException,
SkipObjectException,
)
from authentik.core.models import User
from authentik.enterprise.providers.google_workspace.clients.base import GoogleWorkspaceSyncClient
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceDeleteAction,
GoogleWorkspaceProvider,
GoogleWorkspaceProviderMapping,
GoogleWorkspaceProviderUser,
)
from authentik.events.models import Event, EventAction
from authentik.lib.sync.mapper import PropertyMappingManager
from authentik.lib.sync.outgoing.exceptions import (
ObjectExistsSyncException,
StopSync,
TransientSyncException,
)
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.policies.utils import delete_none_values
@ -29,37 +23,17 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
connection_type_query = "user"
can_discover = True
def to_schema(self, obj: User) -> dict:
def __init__(self, provider: GoogleWorkspaceProvider) -> None:
super().__init__(provider)
self.mapper = PropertyMappingManager(
self.provider.property_mappings.all().order_by("name").select_subclasses(),
GoogleWorkspaceProviderMapping,
["provider", "connection"],
)
def to_schema(self, obj: User, connection: GoogleWorkspaceProviderUser) -> dict:
"""Convert authentik user"""
raw_google_user = {}
for mapping in self.provider.property_mappings.all().order_by("name").select_subclasses():
if not isinstance(mapping, GoogleWorkspaceProviderMapping):
continue
try:
mapping: GoogleWorkspaceProviderMapping
value = mapping.evaluate(
user=obj,
request=None,
provider=self.provider,
)
if value is None:
continue
always_merger.merge(raw_google_user, value)
except SkipObjectException as exc:
raise exc from exc
except (PropertyMappingExpressionException, ValueError) as exc:
# Value error can be raised when assigning invalid data to an attribute
Event.new(
EventAction.CONFIGURATION_ERROR,
message=f"Failed to evaluate property-mapping {exception_to_string(exc)}",
mapping=mapping,
).save()
raise StopSync(exc, obj, mapping) from exc
if not raw_google_user:
raise StopSync(ValueError("No user mappings configured"), obj)
if "primaryEmail" not in raw_google_user:
raw_google_user["primaryEmail"] = str(obj.email)
return delete_none_values(raw_google_user)
return delete_none_values(super().to_schema(obj, connection, primaryEmail=obj.email))
def delete(self, obj: User):
"""Delete user"""
@ -71,11 +45,11 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
return None
with transaction.atomic():
response = None
if self.provider.user_delete_action == GoogleWorkspaceDeleteAction.DELETE:
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
response = self._request(
self.directory_service.users().delete(userKey=google_user.google_id)
)
elif self.provider.user_delete_action == GoogleWorkspaceDeleteAction.SUSPEND:
elif self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
response = self._request(
self.directory_service.users().update(
userKey=google_user.google_id, body={"suspended": True}
@ -86,7 +60,7 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
def create(self, user: User):
"""Create user from scratch and create a connection object"""
google_user = self.to_schema(user)
google_user = self.to_schema(user, None)
self.check_email_valid(
google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
)
@ -95,25 +69,30 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
response = self._request(self.directory_service.users().insert(body=google_user))
except ObjectExistsSyncException:
# user already exists in google workspace, so we can connect them manually
GoogleWorkspaceProviderUser.objects.create(
provider=self.provider, user=user, google_id=user.email
return GoogleWorkspaceProviderUser.objects.create(
provider=self.provider, user=user, google_id=user.email, attributes={}
)
except TransientSyncException as exc:
raise exc
else:
GoogleWorkspaceProviderUser.objects.create(
provider=self.provider, user=user, google_id=response["primaryEmail"]
return GoogleWorkspaceProviderUser.objects.create(
provider=self.provider,
user=user,
google_id=response["primaryEmail"],
attributes=response,
)
def update(self, user: User, connection: GoogleWorkspaceProviderUser):
"""Update existing user"""
google_user = self.to_schema(user)
google_user = self.to_schema(user, connection)
self.check_email_valid(
google_user["primaryEmail"], *[x["address"] for x in google_user.get("emails", [])]
)
self._request(
response = self._request(
self.directory_service.users().update(userKey=connection.google_id, body=google_user)
)
connection.attributes = response
connection.save()
def discover(self):
"""Iterate through all users and connect them with authentik users if possible"""
@ -138,4 +117,5 @@ class GoogleWorkspaceUserClient(GoogleWorkspaceSyncClient[User, GoogleWorkspaceP
provider=self.provider,
user=matching_authentik_user,
google_id=email,
attributes=user,
)

View File

@ -0,0 +1,179 @@
# Generated by Django 5.0.6 on 2024-05-09 12:57
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
replaces = [
("authentik_providers_google_workspace", "0001_initial"),
(
"authentik_providers_google_workspace",
"0002_alter_googleworkspaceprovidergroup_options_and_more",
),
]
initial = True
dependencies = [
("authentik_core", "0035_alter_group_options_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="GoogleWorkspaceProviderMapping",
fields=[
(
"propertymapping_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.propertymapping",
),
),
],
options={
"verbose_name": "Google Workspace Provider Mapping",
"verbose_name_plural": "Google Workspace Provider Mappings",
},
bases=("authentik_core.propertymapping",),
),
migrations.CreateModel(
name="GoogleWorkspaceProvider",
fields=[
(
"provider_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.provider",
),
),
("delegated_subject", models.EmailField(max_length=254)),
("credentials", models.JSONField()),
(
"scopes",
models.TextField(
default="https://www.googleapis.com/auth/admin.directory.user,https://www.googleapis.com/auth/admin.directory.group,https://www.googleapis.com/auth/admin.directory.group.member,https://www.googleapis.com/auth/admin.directory.domain.readonly"
),
),
("default_group_email_domain", models.TextField()),
("exclude_users_service_account", models.BooleanField(default=False)),
(
"user_delete_action",
models.TextField(
choices=[
("do_nothing", "Do Nothing"),
("delete", "Delete"),
("suspend", "Suspend"),
],
default="delete",
),
),
(
"group_delete_action",
models.TextField(
choices=[
("do_nothing", "Do Nothing"),
("delete", "Delete"),
("suspend", "Suspend"),
],
default="delete",
),
),
(
"filter_group",
models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_core.group",
),
),
(
"property_mappings_group",
models.ManyToManyField(
blank=True,
default=None,
help_text="Property mappings used for group creation/updating.",
to="authentik_core.propertymapping",
),
),
],
options={
"verbose_name": "Google Workspace Provider",
"verbose_name_plural": "Google Workspace Providers",
},
bases=("authentik_core.provider", models.Model),
),
migrations.CreateModel(
name="GoogleWorkspaceProviderGroup",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("google_id", models.TextField()),
(
"group",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="authentik_core.group"
),
),
(
"provider",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="authentik_providers_google_workspace.googleworkspaceprovider",
),
),
],
options={
"unique_together": {("google_id", "group", "provider")},
"verbose_name": "Google Workspace Provider Group",
"verbose_name_plural": "Google Workspace Provider Groups",
},
),
migrations.CreateModel(
name="GoogleWorkspaceProviderUser",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("google_id", models.TextField()),
(
"provider",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="authentik_providers_google_workspace.googleworkspaceprovider",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"unique_together": {("google_id", "user", "provider")},
"verbose_name": "Google Workspace Provider User",
"verbose_name_plural": "Google Workspace Provider Users",
},
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 5.0.6 on 2024-05-08 14:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_google_workspace", "0001_initial"),
]
operations = [
migrations.AlterModelOptions(
name="googleworkspaceprovidergroup",
options={
"verbose_name": "Google Workspace Provider Group",
"verbose_name_plural": "Google Workspace Provider Groups",
},
),
migrations.AlterModelOptions(
name="googleworkspaceprovideruser",
options={
"verbose_name": "Google Workspace Provider User",
"verbose_name_plural": "Google Workspace Provider Users",
},
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 5.0.6 on 2024-05-23 20:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"authentik_providers_google_workspace",
"0001_squashed_0002_alter_googleworkspaceprovidergroup_options_and_more",
),
]
operations = [
migrations.AddField(
model_name="googleworkspaceprovidergroup",
name="attributes",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="googleworkspaceprovideruser",
name="attributes",
field=models.JSONField(default=dict),
),
]

View File

@ -5,6 +5,7 @@ from uuid import uuid4
from django.db import models
from django.db.models import QuerySet
from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _
from google.oauth2.service_account import Credentials
from rest_framework.serializers import Serializer
@ -16,8 +17,9 @@ from authentik.core.models import (
User,
UserTypes,
)
from authentik.lib.models import SerializerModel
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction, OutgoingSyncProvider
def default_scopes() -> list[str]:
@ -29,15 +31,6 @@ def default_scopes() -> list[str]:
]
class GoogleWorkspaceDeleteAction(models.TextChoices):
"""Action taken when a user/group is deleted in authentik. Suspend is not available for groups,
and will be treated as `do_nothing`"""
DO_NOTHING = "do_nothing"
DELETE = "delete"
SUSPEND = "suspend"
class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
"""Sync users from authentik into Google Workspace."""
@ -48,10 +41,10 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
default_group_email_domain = models.TextField()
exclude_users_service_account = models.BooleanField(default=False)
user_delete_action = models.TextField(
choices=GoogleWorkspaceDeleteAction.choices, default=GoogleWorkspaceDeleteAction.DELETE
choices=OutgoingSyncDeleteAction.choices, default=OutgoingSyncDeleteAction.DELETE
)
group_delete_action = models.TextField(
choices=GoogleWorkspaceDeleteAction.choices, default=GoogleWorkspaceDeleteAction.DELETE
choices=OutgoingSyncDeleteAction.choices, default=OutgoingSyncDeleteAction.DELETE
)
filter_group = models.ForeignKey(
@ -106,6 +99,10 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
).with_subject(self.delegated_subject),
}
@property
def icon_url(self) -> str | None:
return static("authentik/sources/google.svg")
@property
def component(self) -> str:
return "ak-provider-google-workspace-form"
@ -113,10 +110,10 @@ class GoogleWorkspaceProvider(OutgoingSyncProvider, BackchannelProvider):
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.providers import (
GoogleProviderSerializer,
GoogleWorkspaceProviderSerializer,
)
return GoogleProviderSerializer
return GoogleWorkspaceProviderSerializer
def __str__(self):
return f"Google Workspace Provider {self.name}"
@ -136,10 +133,10 @@ class GoogleWorkspaceProviderMapping(PropertyMapping):
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.property_mappings import (
GoogleProviderMappingSerializer,
GoogleWorkspaceProviderMappingSerializer,
)
return GoogleProviderMappingSerializer
return GoogleWorkspaceProviderMappingSerializer
def __str__(self):
return f"Google Workspace Provider Mapping {self.name}"
@ -149,31 +146,53 @@ class GoogleWorkspaceProviderMapping(PropertyMapping):
verbose_name_plural = _("Google Workspace Provider Mappings")
class GoogleWorkspaceProviderUser(models.Model):
class GoogleWorkspaceProviderUser(SerializerModel):
"""Mapping of a user and provider to a Google user ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.users import (
GoogleWorkspaceProviderUserSerializer,
)
return GoogleWorkspaceProviderUserSerializer
class Meta:
verbose_name = _("Google Workspace Provider User")
verbose_name_plural = _("Google Workspace Provider Users")
unique_together = (("google_id", "user", "provider"),)
def __str__(self) -> str:
return f"Google Workspace User {self.user_id} to {self.provider_id}"
return f"Google Workspace Provider User {self.user_id} to {self.provider_id}"
class GoogleWorkspaceProviderGroup(models.Model):
class GoogleWorkspaceProviderGroup(SerializerModel):
"""Mapping of a group and provider to a Google group ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
google_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey(GoogleWorkspaceProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.google_workspace.api.groups import (
GoogleWorkspaceProviderGroupSerializer,
)
return GoogleWorkspaceProviderGroupSerializer
class Meta:
verbose_name = _("Google Workspace Provider Group")
verbose_name_plural = _("Google Workspace Provider Groups")
unique_together = (("google_id", "group", "provider"),)
def __str__(self) -> str:
return f"Google Workspace Group {self.group_id} to {self.provider_id}"
return f"Google Workspace Provider Group {self.group_id} to {self.provider_id}"

View File

@ -2,18 +2,21 @@
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider
from authentik.events.system_tasks import SystemTask
from authentik.lib.sync.outgoing.exceptions import TransientSyncException
from authentik.lib.sync.outgoing.tasks import SyncTasks
from authentik.root.celery import CELERY_APP
sync_tasks = SyncTasks(GoogleWorkspaceProvider)
@CELERY_APP.task()
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def google_workspace_sync_objects(*args, **kwargs):
return sync_tasks.sync_objects(*args, **kwargs)
@CELERY_APP.task(base=SystemTask, bind=True)
@CELERY_APP.task(
base=SystemTask, bind=True, autoretry_for=(TransientSyncException,), retry_backoff=True
)
def google_workspace_sync(self, provider_pk: int, *args, **kwargs):
"""Run full sync for Google Workspace provider"""
return sync_tasks.sync_single(self, provider_pk, google_workspace_sync_objects)
@ -24,11 +27,11 @@ def google_workspace_sync_all():
return sync_tasks.sync_all(google_workspace_sync)
@CELERY_APP.task()
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def google_workspace_sync_direct(*args, **kwargs):
return sync_tasks.sync_signal_direct(*args, **kwargs)
@CELERY_APP.task()
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def google_workspace_sync_m2m(*args, **kwargs):
return sync_tasks.sync_signal_m2m(*args, **kwargs)

View File

@ -9,7 +9,6 @@ from authentik.core.models import Application, Group, User
from authentik.core.tests.utils import create_test_user
from authentik.enterprise.providers.google_workspace.clients.test_http import MockHTTP
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceDeleteAction,
GoogleWorkspaceProvider,
GoogleWorkspaceProviderGroup,
GoogleWorkspaceProviderMapping,
@ -17,6 +16,7 @@ from authentik.enterprise.providers.google_workspace.models import (
from authentik.enterprise.providers.google_workspace.tasks import google_workspace_sync
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.lib.tests.utils import load_fixture
from authentik.tenants.models import Tenant
@ -82,6 +82,27 @@ class GoogleWorkspaceGroupTests(TestCase):
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 2)
def test_group_not_created(self):
"""Test without group property mappings, no group is created"""
self.provider.property_mappings_group.clear()
uid = generate_id()
http = MockHTTP()
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/customer/my_customer/domains?key={self.api_key}&alt=json",
domains_list_v1_mock,
)
with patch(
"authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProvider.google_credentials",
MagicMock(return_value={"developerKey": self.api_key, "http": http}),
):
group = Group.objects.create(name=uid)
google_group = GoogleWorkspaceProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNone(google_group)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 1)
def test_group_create_update(self):
"""Test group updating"""
uid = generate_id()
@ -240,7 +261,7 @@ class GoogleWorkspaceGroupTests(TestCase):
def test_group_create_delete_do_nothing(self):
"""Test group deletion (delete action = do nothing)"""
self.provider.group_delete_action = GoogleWorkspaceDeleteAction.DO_NOTHING
self.provider.group_delete_action = OutgoingSyncDeleteAction.DO_NOTHING
self.provider.save()
uid = generate_id()
http = MockHTTP()

View File

@ -9,7 +9,6 @@ from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.enterprise.providers.google_workspace.clients.test_http import MockHTTP
from authentik.enterprise.providers.google_workspace.models import (
GoogleWorkspaceDeleteAction,
GoogleWorkspaceProvider,
GoogleWorkspaceProviderMapping,
GoogleWorkspaceProviderUser,
@ -17,6 +16,7 @@ from authentik.enterprise.providers.google_workspace.models import (
from authentik.enterprise.providers.google_workspace.tasks import google_workspace_sync
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.lib.tests.utils import load_fixture
from authentik.tenants.models import Tenant
@ -86,6 +86,31 @@ class GoogleWorkspaceUserTests(TestCase):
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 2)
def test_user_not_created(self):
"""Test without property mappings, no group is created"""
self.provider.property_mappings.clear()
uid = generate_id()
http = MockHTTP()
http.add_response(
f"https://admin.googleapis.com/admin/directory/v1/customer/my_customer/domains?key={self.api_key}&alt=json",
domains_list_v1_mock,
)
with patch(
"authentik.enterprise.providers.google_workspace.models.GoogleWorkspaceProvider.google_credentials",
MagicMock(return_value={"developerKey": self.api_key, "http": http}),
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
google_user = GoogleWorkspaceProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNone(google_user)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
self.assertEqual(len(http.requests()), 1)
def test_user_create_update(self):
"""Test user updating"""
uid = generate_id()
@ -160,7 +185,7 @@ class GoogleWorkspaceUserTests(TestCase):
def test_user_create_delete_suspend(self):
"""Test user deletion (delete action = Suspend)"""
self.provider.user_delete_action = GoogleWorkspaceDeleteAction.SUSPEND
self.provider.user_delete_action = OutgoingSyncDeleteAction.SUSPEND
self.provider.save()
uid = generate_id()
http = MockHTTP()
@ -209,7 +234,7 @@ class GoogleWorkspaceUserTests(TestCase):
def test_user_create_delete_do_nothing(self):
"""Test user deletion (delete action = do nothing)"""
self.provider.user_delete_action = GoogleWorkspaceDeleteAction.DO_NOTHING
self.provider.user_delete_action = OutgoingSyncDeleteAction.DO_NOTHING
self.provider.save()
uid = generate_id()
http = MockHTTP()

View File

@ -1,11 +1,21 @@
"""google provider urls"""
from authentik.enterprise.providers.google_workspace.api.property_mappings import (
GoogleProviderMappingViewSet,
from authentik.enterprise.providers.google_workspace.api.groups import (
GoogleWorkspaceProviderGroupViewSet,
)
from authentik.enterprise.providers.google_workspace.api.property_mappings import (
GoogleWorkspaceProviderMappingViewSet,
)
from authentik.enterprise.providers.google_workspace.api.providers import (
GoogleWorkspaceProviderViewSet,
)
from authentik.enterprise.providers.google_workspace.api.users import (
GoogleWorkspaceProviderUserViewSet,
)
from authentik.enterprise.providers.google_workspace.api.providers import GoogleProviderViewSet
api_urlpatterns = [
("providers/google_workspace", GoogleProviderViewSet),
("propertymappings/provider/google_workspace", GoogleProviderMappingViewSet),
("providers/google_workspace", GoogleWorkspaceProviderViewSet),
("providers/google_workspace_users", GoogleWorkspaceProviderUserViewSet),
("providers/google_workspace_groups", GoogleWorkspaceProviderGroupViewSet),
("propertymappings/provider/google_workspace", GoogleWorkspaceProviderMappingViewSet),
]

View File

@ -0,0 +1,45 @@
"""MicrosoftEntraProviderGroup API Views"""
from rest_framework import mixins
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserGroupSerializer
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderGroup
class MicrosoftEntraProviderGroupSerializer(ModelSerializer):
"""MicrosoftEntraProviderGroup Serializer"""
group_obj = UserGroupSerializer(source="group", read_only=True)
class Meta:
model = MicrosoftEntraProviderGroup
fields = [
"id",
"microsoft_id",
"group",
"group_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class MicrosoftEntraProviderGroupViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""MicrosoftEntraProviderGroup Viewset"""
queryset = MicrosoftEntraProviderGroup.objects.all().select_related("group")
serializer_class = MicrosoftEntraProviderGroupSerializer
filterset_fields = ["provider__id", "group__name", "group__group_uuid"]
search_fields = ["provider__name", "group__name"]
ordering = ["group__name"]

View File

@ -0,0 +1,39 @@
"""microsoft Property mappings API Views"""
from django_filters.filters import AllValuesMultipleFilter
from django_filters.filterset import FilterSet
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.property_mappings import PropertyMappingSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderMapping
class MicrosoftEntraProviderMappingSerializer(PropertyMappingSerializer):
"""MicrosoftEntraProviderMapping Serializer"""
class Meta:
model = MicrosoftEntraProviderMapping
fields = PropertyMappingSerializer.Meta.fields
class MicrosoftEntraProviderMappingFilter(FilterSet):
"""Filter for MicrosoftEntraProviderMapping"""
managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed"))
class Meta:
model = MicrosoftEntraProviderMapping
fields = "__all__"
class MicrosoftEntraProviderMappingViewSet(UsedByMixin, ModelViewSet):
"""MicrosoftEntraProviderMapping Viewset"""
queryset = MicrosoftEntraProviderMapping.objects.all()
serializer_class = MicrosoftEntraProviderMappingSerializer
filterset_class = MicrosoftEntraProviderMappingFilter
search_fields = ["name"]
ordering = ["name"]

View File

@ -0,0 +1,52 @@
"""Microsoft Provider API Views"""
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.api import EnterpriseRequiredMixin
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
class MicrosoftEntraProviderSerializer(EnterpriseRequiredMixin, ProviderSerializer):
"""MicrosoftEntraProvider Serializer"""
class Meta:
model = MicrosoftEntraProvider
fields = [
"pk",
"name",
"property_mappings",
"property_mappings_group",
"component",
"assigned_backchannel_application_slug",
"assigned_backchannel_application_name",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
"client_id",
"client_secret",
"tenant_id",
"exclude_users_service_account",
"filter_group",
"user_delete_action",
"group_delete_action",
]
extra_kwargs = {}
class MicrosoftEntraProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelViewSet):
"""MicrosoftEntraProvider Viewset"""
queryset = MicrosoftEntraProvider.objects.all()
serializer_class = MicrosoftEntraProviderSerializer
filterset_fields = [
"name",
"exclude_users_service_account",
"filter_group",
]
search_fields = ["name"]
ordering = ["name"]
sync_single_task = microsoft_entra_sync

View File

@ -0,0 +1,45 @@
"""MicrosoftEntraProviderUser API Views"""
from rest_framework import mixins
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet
from authentik.core.api.groups import GroupMemberSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProviderUser
class MicrosoftEntraProviderUserSerializer(ModelSerializer):
"""MicrosoftEntraProviderUser Serializer"""
user_obj = GroupMemberSerializer(source="user", read_only=True)
class Meta:
model = MicrosoftEntraProviderUser
fields = [
"id",
"microsoft_id",
"user",
"user_obj",
"provider",
"attributes",
]
extra_kwargs = {"attributes": {"read_only": True}}
class MicrosoftEntraProviderUserViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
mixins.ListModelMixin,
GenericViewSet,
):
"""MicrosoftEntraProviderUser Viewset"""
queryset = MicrosoftEntraProviderUser.objects.all().select_related("user")
serializer_class = MicrosoftEntraProviderUserSerializer
filterset_fields = ["provider__id", "user__username", "user__id"]
search_fields = ["provider__name", "user__username"]
ordering = ["user__username"]

View File

@ -0,0 +1,9 @@
from authentik.enterprise.apps import EnterpriseConfig
class AuthentikEnterpriseProviderMicrosoftEntraConfig(EnterpriseConfig):
name = "authentik.enterprise.providers.microsoft_entra"
label = "authentik_providers_microsoft_entra"
verbose_name = "authentik Enterprise.Providers.Microsoft Entra"
default = True

View File

@ -0,0 +1,110 @@
from asyncio import run
from collections.abc import Coroutine
from dataclasses import asdict
from typing import Any
from azure.core.exceptions import (
ClientAuthenticationError,
ServiceRequestError,
ServiceResponseError,
)
from azure.identity.aio import ClientSecretCredential
from django.db.models import Model
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from kiota_abstractions.api_error import APIError
from kiota_authentication_azure.azure_identity_authentication_provider import (
AzureIdentityAuthenticationProvider,
)
from kiota_http.kiota_client_factory import KiotaClientFactory
from msgraph.generated.models.entity import Entity
from msgraph.generated.models.o_data_errors.o_data_error import ODataError
from msgraph.graph_request_adapter import GraphRequestAdapter, options
from msgraph.graph_service_client import GraphServiceClient
from msgraph_core import GraphClientFactory
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
from authentik.events.utils import sanitize_item
from authentik.lib.sync.outgoing import HTTP_CONFLICT
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.exceptions import (
BadRequestSyncException,
NotFoundSyncException,
ObjectExistsSyncException,
StopSync,
TransientSyncException,
)
def get_request_adapter(
credentials: ClientSecretCredential, scopes: list[str] | None = None
) -> GraphRequestAdapter:
if scopes:
auth_provider = AzureIdentityAuthenticationProvider(credentials=credentials, scopes=scopes)
else:
auth_provider = AzureIdentityAuthenticationProvider(credentials=credentials)
return GraphRequestAdapter(
auth_provider=auth_provider,
client=GraphClientFactory.create_with_default_middleware(
options=options, client=KiotaClientFactory.get_default_client()
),
)
class MicrosoftEntraSyncClient[TModel: Model, TConnection: Model, TSchema: dict](
BaseOutgoingSyncClient[TModel, TConnection, TSchema, MicrosoftEntraProvider]
):
"""Base client for syncing to microsoft entra"""
domains: list
def __init__(self, provider: MicrosoftEntraProvider) -> None:
super().__init__(provider)
self.credentials = provider.microsoft_credentials()
self.__prefetch_domains()
@property
def client(self):
return GraphServiceClient(request_adapter=get_request_adapter(**self.credentials))
def _request[T](self, request: Coroutine[Any, Any, T]) -> T:
try:
return run(request)
except ClientAuthenticationError as exc:
raise StopSync(exc, None, None) from exc
except ODataError as exc:
raise StopSync(exc, None, None) from exc
except (ServiceRequestError, ServiceResponseError) as exc:
raise TransientSyncException("Failed to sent request") from exc
except APIError as exc:
if exc.response_status_code == HttpResponseNotFound.status_code:
raise NotFoundSyncException("Object not found") from exc
if exc.response_status_code == HttpResponseBadRequest.status_code:
raise BadRequestSyncException("Bad request", exc.response_headers) from exc
if exc.response_status_code == HTTP_CONFLICT:
raise ObjectExistsSyncException("Object exists", exc.response_headers) from exc
raise exc
def __prefetch_domains(self):
self.domains = []
organizations = self._request(self.client.organization.get())
next_link = True
while next_link:
for org in organizations.value:
self.domains.extend([x.name for x in org.verified_domains])
next_link = organizations.odata_next_link
if not next_link:
break
organizations = self._request(self.client.organization.with_url(next_link).get())
def check_email_valid(self, *emails: str):
for email in emails:
if not any(email.endswith(f"@{domain_name}") for domain_name in self.domains):
raise BadRequestSyncException(f"Invalid email domain: {email}")
def entity_as_dict(self, entity: Entity) -> dict:
"""Create a dictionary of a model instance, making sure to remove (known) things
we can't JSON serialize"""
raw_data = asdict(entity)
raw_data.pop("backing_store", None)
return sanitize_item(raw_data)

View File

@ -0,0 +1,228 @@
from deepmerge import always_merger
from django.db import transaction
from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
from msgraph.generated.models.group import Group as MSGroup
from msgraph.generated.models.reference_create import ReferenceCreate
from authentik.core.models import Group
from authentik.enterprise.providers.microsoft_entra.clients.base import MicrosoftEntraSyncClient
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProvider,
MicrosoftEntraProviderGroup,
MicrosoftEntraProviderMapping,
MicrosoftEntraProviderUser,
)
from authentik.lib.sync.mapper import PropertyMappingManager
from authentik.lib.sync.outgoing.base import Direction
from authentik.lib.sync.outgoing.exceptions import (
NotFoundSyncException,
ObjectExistsSyncException,
StopSync,
TransientSyncException,
)
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
class MicrosoftEntraGroupClient(
MicrosoftEntraSyncClient[Group, MicrosoftEntraProviderGroup, MSGroup]
):
"""Microsoft client for groups"""
connection_type = MicrosoftEntraProviderGroup
connection_type_query = "group"
can_discover = True
def __init__(self, provider: MicrosoftEntraProvider) -> None:
super().__init__(provider)
self.mapper = PropertyMappingManager(
self.provider.property_mappings_group.all().order_by("name").select_subclasses(),
MicrosoftEntraProviderMapping,
["group", "provider", "connection"],
)
def to_schema(self, obj: Group, connection: MicrosoftEntraProviderGroup) -> MSGroup:
"""Convert authentik group"""
raw_microsoft_group = super().to_schema(obj, connection)
try:
return MSGroup(**raw_microsoft_group)
except TypeError as exc:
raise StopSync(exc, obj) from exc
def delete(self, obj: Group):
"""Delete group"""
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=obj
).first()
if not microsoft_group:
self.logger.debug("Group does not exist in Microsoft, skipping")
return None
with transaction.atomic():
if self.provider.group_delete_action == OutgoingSyncDeleteAction.DELETE:
self._request(self.client.groups.by_group_id(microsoft_group.microsoft_id).delete())
microsoft_group.delete()
def create(self, group: Group):
"""Create group from scratch and create a connection object"""
microsoft_group = self.to_schema(group, None)
with transaction.atomic():
try:
response = self._request(self.client.groups.post(microsoft_group))
except ObjectExistsSyncException:
# group already exists in microsoft entra, so we can connect them manually
# for groups we need to fetch the group from microsoft as we connect on
# ID and not group email
query_params = GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
filter=f"displayName eq '{microsoft_group.display_name}'",
)
request_configuration = (
GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
query_parameters=query_params,
)
)
group_data = self._request(self.client.groups.get(request_configuration))
if group_data.odata_count < 1 or len(group_data.value) < 1:
self.logger.warning(
"Group which could not be created also does not exist", group=group
)
return
ms_group = group_data.value[0]
return MicrosoftEntraProviderGroup.objects.create(
provider=self.provider,
group=group,
microsoft_id=ms_group.id,
attributes=self.entity_as_dict(ms_group),
)
else:
return MicrosoftEntraProviderGroup.objects.create(
provider=self.provider,
group=group,
microsoft_id=response.id,
attributes=self.entity_as_dict(response),
)
def update(self, group: Group, connection: MicrosoftEntraProviderGroup):
"""Update existing group"""
microsoft_group = self.to_schema(group, connection)
microsoft_group.id = connection.microsoft_id
try:
response = self._request(
self.client.groups.by_group_id(connection.microsoft_id).patch(microsoft_group)
)
if response:
always_merger.merge(connection.attributes, self.entity_as_dict(response))
connection.save()
except NotFoundSyncException:
# Resource missing is handled by self.write, which will re-create the group
raise
def write(self, obj: Group):
microsoft_group, created = super().write(obj)
self.create_sync_members(obj, microsoft_group)
return microsoft_group, created
def create_sync_members(self, obj: Group, microsoft_group: MicrosoftEntraProviderGroup):
"""Sync all members after a group was created"""
users = list(obj.users.order_by("id").values_list("id", flat=True))
connections = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user__pk__in=users
).values_list("microsoft_id", flat=True)
self._patch(microsoft_group.microsoft_id, Direction.add, connections)
def update_group(self, group: Group, action: Direction, users_set: set[int]):
"""Update a groups members"""
if action == Direction.add:
return self._patch_add_users(group, users_set)
if action == Direction.remove:
return self._patch_remove_users(group, users_set)
def _patch(self, microsoft_group_id: str, direction: Direction, members: list[str]):
for user in members:
try:
if direction == Direction.add:
request_body = ReferenceCreate(
odata_id=f"https://graph.microsoft.com/v1.0/directoryObjects/{user}",
)
self._request(
self.client.groups.by_group_id(microsoft_group_id).members.ref.post(
request_body
)
)
if direction == Direction.remove:
self._request(
self.client.groups.by_group_id(microsoft_group_id)
.members.by_directory_object_id(user)
.ref.delete()
)
except ObjectExistsSyncException:
pass
except TransientSyncException:
raise
def _patch_add_users(self, group: Group, users_set: set[int]):
"""Add users in users_set to group"""
if len(users_set) < 1:
return
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
if not microsoft_group:
self.logger.warning(
"could not sync group membership, group does not exist", group=group
)
return
user_ids = list(
MicrosoftEntraProviderUser.objects.filter(
user__pk__in=users_set, provider=self.provider
).values_list("microsoft_id", flat=True)
)
if len(user_ids) < 1:
return
self._patch(microsoft_group.microsoft_id, Direction.add, user_ids)
def _patch_remove_users(self, group: Group, users_set: set[int]):
"""Remove users in users_set from group"""
if len(users_set) < 1:
return
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
if not microsoft_group:
self.logger.warning(
"could not sync group membership, group does not exist", group=group
)
return
user_ids = list(
MicrosoftEntraProviderUser.objects.filter(
user__pk__in=users_set, provider=self.provider
).values_list("microsoft_id", flat=True)
)
if len(user_ids) < 1:
return
self._patch(microsoft_group.microsoft_id, Direction.remove, user_ids)
def discover(self):
"""Iterate through all groups and connect them with authentik groups if possible"""
groups = self._request(self.client.groups.get())
next_link = True
while next_link:
for group in groups.value:
self._discover_single_group(group)
next_link = groups.odata_next_link
if not next_link:
break
groups = self._request(self.client.groups.with_url(next_link).get())
def _discover_single_group(self, group: MSGroup):
"""handle discovery of a single group"""
microsoft_name = group.unique_name
matching_authentik_group = (
self.provider.get_object_qs(Group).filter(name=microsoft_name).first()
)
if not matching_authentik_group:
return
MicrosoftEntraProviderGroup.objects.get_or_create(
provider=self.provider,
group=matching_authentik_group,
microsoft_id=group.id,
attributes=self.entity_as_dict(group),
)

View File

@ -0,0 +1,143 @@
from deepmerge import always_merger
from django.db import transaction
from msgraph.generated.models.user import User as MSUser
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
from authentik.core.models import User
from authentik.enterprise.providers.microsoft_entra.clients.base import MicrosoftEntraSyncClient
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProvider,
MicrosoftEntraProviderMapping,
MicrosoftEntraProviderUser,
)
from authentik.lib.sync.mapper import PropertyMappingManager
from authentik.lib.sync.outgoing.exceptions import (
ObjectExistsSyncException,
StopSync,
TransientSyncException,
)
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.policies.utils import delete_none_values
class MicrosoftEntraUserClient(MicrosoftEntraSyncClient[User, MicrosoftEntraProviderUser, MSUser]):
"""Sync authentik users into microsoft entra"""
connection_type = MicrosoftEntraProviderUser
connection_type_query = "user"
can_discover = True
def __init__(self, provider: MicrosoftEntraProvider) -> None:
super().__init__(provider)
self.mapper = PropertyMappingManager(
self.provider.property_mappings.all().order_by("name").select_subclasses(),
MicrosoftEntraProviderMapping,
["provider", "connection"],
)
def to_schema(self, obj: User, connection: MicrosoftEntraProviderUser) -> MSUser:
"""Convert authentik user"""
raw_microsoft_user = super().to_schema(obj, connection)
try:
return MSUser(**delete_none_values(raw_microsoft_user))
except TypeError as exc:
raise StopSync(exc, obj) from exc
def delete(self, obj: User):
"""Delete user"""
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=obj
).first()
if not microsoft_user:
self.logger.debug("User does not exist in Microsoft, skipping")
return None
with transaction.atomic():
response = None
if self.provider.user_delete_action == OutgoingSyncDeleteAction.DELETE:
response = self._request(
self.client.users.by_user_id(microsoft_user.microsoft_id).delete()
)
elif self.provider.user_delete_action == OutgoingSyncDeleteAction.SUSPEND:
response = self._request(
self.client.users.by_user_id(microsoft_user.microsoft_id).patch(
MSUser(account_enabled=False)
)
)
microsoft_user.delete()
return response
def create(self, user: User):
"""Create user from scratch and create a connection object"""
microsoft_user = self.to_schema(user, None)
self.check_email_valid(microsoft_user.user_principal_name)
with transaction.atomic():
try:
response = self._request(self.client.users.post(microsoft_user))
except ObjectExistsSyncException:
# user already exists in microsoft entra, so we can connect them manually
query_params = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters()(
filter=f"mail eq '{microsoft_user.mail}'",
)
request_configuration = (
UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
query_parameters=query_params,
)
)
user_data = self._request(self.client.users.get(request_configuration))
if user_data.odata_count < 1 or len(user_data.value) < 1:
self.logger.warning(
"User which could not be created also does not exist", user=user
)
return
ms_user = user_data.value[0]
return MicrosoftEntraProviderUser.objects.create(
provider=self.provider,
user=user,
microsoft_id=ms_user.id,
attributes=self.entity_as_dict(ms_user),
)
except TransientSyncException as exc:
raise exc
else:
print(self.entity_as_dict(response))
return MicrosoftEntraProviderUser.objects.create(
provider=self.provider,
user=user,
microsoft_id=response.id,
attributes=self.entity_as_dict(response),
)
def update(self, user: User, connection: MicrosoftEntraProviderUser):
"""Update existing user"""
microsoft_user = self.to_schema(user, connection)
self.check_email_valid(microsoft_user.user_principal_name)
response = self._request(
self.client.users.by_user_id(connection.microsoft_id).patch(microsoft_user)
)
if response:
always_merger.merge(connection.attributes, self.entity_as_dict(response))
connection.save()
def discover(self):
"""Iterate through all users and connect them with authentik users if possible"""
users = self._request(self.client.users.get())
next_link = True
while next_link:
for user in users.value:
self._discover_single_user(user)
next_link = users.odata_next_link
if not next_link:
break
users = self._request(self.client.users.with_url(next_link).get())
def _discover_single_user(self, user: MSUser):
"""handle discovery of a single user"""
matching_authentik_user = self.provider.get_object_qs(User).filter(email=user.mail).first()
if not matching_authentik_user:
return
MicrosoftEntraProviderUser.objects.get_or_create(
provider=self.provider,
user=matching_authentik_user,
microsoft_id=user.id,
attributes=self.entity_as_dict(user),
)

View File

@ -0,0 +1,165 @@
# Generated by Django 5.0.6 on 2024-05-08 14:35
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("authentik_core", "0035_alter_group_options_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="MicrosoftEntraProviderMapping",
fields=[
(
"propertymapping_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.propertymapping",
),
),
],
options={
"verbose_name": "Microsoft Entra Provider Mapping",
"verbose_name_plural": "Microsoft Entra Provider Mappings",
},
bases=("authentik_core.propertymapping",),
),
migrations.CreateModel(
name="MicrosoftEntraProvider",
fields=[
(
"provider_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.provider",
),
),
("client_id", models.TextField()),
("client_secret", models.TextField()),
("tenant_id", models.TextField()),
("exclude_users_service_account", models.BooleanField(default=False)),
(
"user_delete_action",
models.TextField(
choices=[
("do_nothing", "Do Nothing"),
("delete", "Delete"),
("suspend", "Suspend"),
],
default="delete",
),
),
(
"group_delete_action",
models.TextField(
choices=[
("do_nothing", "Do Nothing"),
("delete", "Delete"),
("suspend", "Suspend"),
],
default="delete",
),
),
(
"filter_group",
models.ForeignKey(
default=None,
null=True,
on_delete=django.db.models.deletion.SET_DEFAULT,
to="authentik_core.group",
),
),
(
"property_mappings_group",
models.ManyToManyField(
blank=True,
default=None,
help_text="Property mappings used for group creation/updating.",
to="authentik_core.propertymapping",
),
),
],
options={
"verbose_name": "Microsoft Entra Provider",
"verbose_name_plural": "Microsoft Entra Providers",
},
bases=("authentik_core.provider", models.Model),
),
migrations.CreateModel(
name="MicrosoftEntraProviderGroup",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("microsoft_id", models.TextField()),
(
"group",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="authentik_core.group"
),
),
(
"provider",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="authentik_providers_microsoft_entra.microsoftentraprovider",
),
),
],
options={
"verbose_name": "Microsoft Entra Provider Group",
"verbose_name_plural": "Microsoft Entra Provider Groups",
"unique_together": {("microsoft_id", "group", "provider")},
},
),
migrations.CreateModel(
name="MicrosoftEntraProviderUser",
fields=[
(
"id",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("microsoft_id", models.TextField()),
(
"provider",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="authentik_providers_microsoft_entra.microsoftentraprovider",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"verbose_name": "Microsoft Entra Provider User",
"verbose_name_plural": "Microsoft Entra Provider User",
"unique_together": {("microsoft_id", "user", "provider")},
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.0.6 on 2024-05-23 20:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_microsoft_entra", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="microsoftentraprovidergroup",
name="attributes",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="microsoftentraprovideruser",
name="attributes",
field=models.JSONField(default=dict),
),
]

View File

@ -0,0 +1,187 @@
"""Microsoft Entra sync provider"""
from typing import Any, Self
from uuid import uuid4
from azure.identity.aio import ClientSecretCredential
from django.db import models
from django.db.models import QuerySet
from django.templatetags.static import static
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.core.models import (
BackchannelProvider,
Group,
PropertyMapping,
User,
UserTypes,
)
from authentik.lib.models import SerializerModel
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction, OutgoingSyncProvider
class MicrosoftEntraProvider(OutgoingSyncProvider, BackchannelProvider):
"""Sync users from authentik into Microsoft Entra."""
client_id = models.TextField()
client_secret = models.TextField()
tenant_id = models.TextField()
exclude_users_service_account = models.BooleanField(default=False)
user_delete_action = models.TextField(
choices=OutgoingSyncDeleteAction.choices, default=OutgoingSyncDeleteAction.DELETE
)
group_delete_action = models.TextField(
choices=OutgoingSyncDeleteAction.choices, default=OutgoingSyncDeleteAction.DELETE
)
filter_group = models.ForeignKey(
"authentik_core.group", on_delete=models.SET_DEFAULT, default=None, null=True
)
property_mappings_group = models.ManyToManyField(
PropertyMapping,
default=None,
blank=True,
help_text=_("Property mappings used for group creation/updating."),
)
def client_for_model(
self, model: type[User | Group]
) -> BaseOutgoingSyncClient[User | Group, Any, Any, Self]:
if issubclass(model, User):
from authentik.enterprise.providers.microsoft_entra.clients.users import (
MicrosoftEntraUserClient,
)
return MicrosoftEntraUserClient(self)
if issubclass(model, Group):
from authentik.enterprise.providers.microsoft_entra.clients.groups import (
MicrosoftEntraGroupClient,
)
return MicrosoftEntraGroupClient(self)
raise ValueError(f"Invalid model {model}")
def get_object_qs(self, type: type[User | Group]) -> QuerySet[User | Group]:
if type == User:
# Get queryset of all users with consistent ordering
# according to the provider's settings
base = User.objects.all().exclude_anonymous()
if self.exclude_users_service_account:
base = base.exclude(type=UserTypes.SERVICE_ACCOUNT).exclude(
type=UserTypes.INTERNAL_SERVICE_ACCOUNT
)
if self.filter_group:
base = base.filter(ak_groups__in=[self.filter_group])
return base.order_by("pk")
if type == Group:
# Get queryset of all groups with consistent ordering
return Group.objects.all().order_by("pk")
raise ValueError(f"Invalid type {type}")
def microsoft_credentials(self):
return {
"credentials": ClientSecretCredential(
self.tenant_id, self.client_id, self.client_secret
)
}
@property
def icon_url(self) -> str | None:
return static("authentik/sources/azuread.svg")
@property
def component(self) -> str:
return "ak-provider-microsoft-entra-form"
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.microsoft_entra.api.providers import (
MicrosoftEntraProviderSerializer,
)
return MicrosoftEntraProviderSerializer
def __str__(self):
return f"Microsoft Entra Provider {self.name}"
class Meta:
verbose_name = _("Microsoft Entra Provider")
verbose_name_plural = _("Microsoft Entra Providers")
class MicrosoftEntraProviderMapping(PropertyMapping):
"""Map authentik data to outgoing Microsoft requests"""
@property
def component(self) -> str:
return "ak-property-mapping-microsoft-entra-form"
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.microsoft_entra.api.property_mappings import (
MicrosoftEntraProviderMappingSerializer,
)
return MicrosoftEntraProviderMappingSerializer
def __str__(self):
return f"Microsoft Entra Provider Mapping {self.name}"
class Meta:
verbose_name = _("Microsoft Entra Provider Mapping")
verbose_name_plural = _("Microsoft Entra Provider Mappings")
class MicrosoftEntraProviderUser(SerializerModel):
"""Mapping of a user and provider to a Microsoft user ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
microsoft_id = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
provider = models.ForeignKey(MicrosoftEntraProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.microsoft_entra.api.users import (
MicrosoftEntraProviderUserSerializer,
)
return MicrosoftEntraProviderUserSerializer
class Meta:
verbose_name = _("Microsoft Entra Provider User")
verbose_name_plural = _("Microsoft Entra Provider User")
unique_together = (("microsoft_id", "user", "provider"),)
def __str__(self) -> str:
return f"Microsoft Entra Provider User {self.user_id} to {self.provider_id}"
class MicrosoftEntraProviderGroup(SerializerModel):
"""Mapping of a group and provider to a Microsoft group ID"""
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
microsoft_id = models.TextField()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
provider = models.ForeignKey(MicrosoftEntraProvider, on_delete=models.CASCADE)
attributes = models.JSONField(default=dict)
@property
def serializer(self) -> type[Serializer]:
from authentik.enterprise.providers.microsoft_entra.api.groups import (
MicrosoftEntraProviderGroupSerializer,
)
return MicrosoftEntraProviderGroupSerializer
class Meta:
verbose_name = _("Microsoft Entra Provider Group")
verbose_name_plural = _("Microsoft Entra Provider Groups")
unique_together = (("microsoft_id", "group", "provider"),)
def __str__(self) -> str:
return f"Microsoft Entra Provider Group {self.group_id} to {self.provider_id}"

View File

@ -0,0 +1,13 @@
"""Microsoft Entra provider task Settings"""
from celery.schedules import crontab
from authentik.lib.utils.time import fqdn_rand
CELERY_BEAT_SCHEDULE = {
"providers_microsoft_entra_sync": {
"task": "authentik.enterprise.providers.microsoft_entra.tasks.microsoft_entra_sync_all",
"schedule": crontab(minute=fqdn_rand("microsoft_entra_sync_all"), hour="*/4"),
"options": {"queue": "authentik_scheduled"},
},
}

View File

@ -0,0 +1,16 @@
"""Microsoft provider signals"""
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
from authentik.enterprise.providers.microsoft_entra.tasks import (
microsoft_entra_sync,
microsoft_entra_sync_direct,
microsoft_entra_sync_m2m,
)
from authentik.lib.sync.outgoing.signals import register_signals
register_signals(
MicrosoftEntraProvider,
task_sync_single=microsoft_entra_sync,
task_sync_direct=microsoft_entra_sync_direct,
task_sync_m2m=microsoft_entra_sync_m2m,
)

View File

@ -0,0 +1,37 @@
"""Microsoft Entra Provider tasks"""
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
from authentik.events.system_tasks import SystemTask
from authentik.lib.sync.outgoing.exceptions import TransientSyncException
from authentik.lib.sync.outgoing.tasks import SyncTasks
from authentik.root.celery import CELERY_APP
sync_tasks = SyncTasks(MicrosoftEntraProvider)
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def microsoft_entra_sync_objects(*args, **kwargs):
return sync_tasks.sync_objects(*args, **kwargs)
@CELERY_APP.task(
base=SystemTask, bind=True, autoretry_for=(TransientSyncException,), retry_backoff=True
)
def microsoft_entra_sync(self, provider_pk: int, *args, **kwargs):
"""Run full sync for Microsoft Entra provider"""
return sync_tasks.sync_single(self, provider_pk, microsoft_entra_sync_objects)
@CELERY_APP.task()
def microsoft_entra_sync_all():
return sync_tasks.sync_all(microsoft_entra_sync)
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def microsoft_entra_sync_direct(*args, **kwargs):
return sync_tasks.sync_signal_direct(*args, **kwargs)
@CELERY_APP.task(autoretry_for=(TransientSyncException,), retry_backoff=True)
def microsoft_entra_sync_m2m(*args, **kwargs):
return sync_tasks.sync_signal_m2m(*args, **kwargs)

View File

@ -0,0 +1,424 @@
"""Microsoft Entra Group tests"""
from unittest.mock import AsyncMock, MagicMock, patch
from azure.identity.aio import ClientSecretCredential
from django.test import TestCase
from msgraph.generated.models.group import Group as MSGroup
from msgraph.generated.models.group_collection_response import GroupCollectionResponse
from msgraph.generated.models.organization import Organization
from msgraph.generated.models.organization_collection_response import OrganizationCollectionResponse
from msgraph.generated.models.user import User as MSUser
from msgraph.generated.models.user_collection_response import UserCollectionResponse
from msgraph.generated.models.verified_domain import VerifiedDomain
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.core.tests.utils import create_test_user
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProvider,
MicrosoftEntraProviderGroup,
MicrosoftEntraProviderMapping,
MicrosoftEntraProviderUser,
)
from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.tenants.models import Tenant
class MicrosoftEntraGroupTests(TestCase):
"""Microsoft Entra Group tests"""
@apply_blueprint("system/providers-microsoft-entra.yaml")
def setUp(self) -> None:
# Delete all groups and groups as the mocked HTTP responses only return one ID
# which will cause errors with multiple groups
Tenant.objects.update(avatars="none")
User.objects.all().exclude_anonymous().delete()
Group.objects.all().delete()
self.provider: MicrosoftEntraProvider = MicrosoftEntraProvider.objects.create(
name=generate_id(),
client_id=generate_id(),
client_secret=generate_id(),
tenant_id=generate_id(),
exclude_users_service_account=True,
)
self.app: Application = Application.objects.create(
name=generate_id(),
slug=generate_id(),
)
self.app.backchannel_providers.add(self.provider)
self.provider.property_mappings.add(
MicrosoftEntraProviderMapping.objects.get(
managed="goauthentik.io/providers/microsoft_entra/user"
)
)
self.provider.property_mappings_group.add(
MicrosoftEntraProviderMapping.objects.get(
managed="goauthentik.io/providers/microsoft_entra/group"
)
)
self.creds = ClientSecretCredential(generate_id(), generate_id(), generate_id())
def test_group_create(self):
"""Test group creation"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=generate_id())),
) as group_create,
):
group = Group.objects.create(name=uid)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
group_create.assert_called_once()
def test_group_not_created(self):
"""Test without group property mappings, no group is created"""
self.provider.property_mappings_group.clear()
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=generate_id())),
) as group_create,
):
group = Group.objects.create(name=uid)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNone(microsoft_group)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
group_create.assert_not_called()
def test_group_create_update(self):
"""Test group updating"""
uid = generate_id()
ext_id = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=ext_id)),
) as group_create,
patch(
"msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch",
AsyncMock(return_value=MSGroup(id=ext_id)),
) as group_patch,
):
group = Group.objects.create(name=uid)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
group.name = "new name"
group.save()
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
group_create.assert_called_once()
group_patch.assert_called_once()
def test_group_create_delete(self):
"""Test group deletion"""
uid = generate_id()
ext_id = generate_id()
with (
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=ext_id)),
) as group_create,
patch(
"msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete",
AsyncMock(return_value=MSGroup(id=ext_id)),
) as group_delete,
):
group = Group.objects.create(name=uid)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
group.delete()
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
group_create.assert_called_once()
group_delete.assert_called_once()
def test_group_create_member_add(self):
"""Test group creation"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=uid)),
) as group_create,
patch(
"msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post",
AsyncMock(),
) as member_add,
):
user = create_test_user(uid)
group = Group.objects.create(name=uid)
group.users.add(user)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_called_once()
group_create.assert_called_once()
member_add.assert_called_once()
self.assertEqual(
member_add.call_args[0][0].odata_id,
f"https://graph.microsoft.com/v1.0/directoryObjects/{MicrosoftEntraProviderUser.objects.filter(
provider=self.provider,
).first().microsoft_id}",
)
def test_group_create_member_remove(self):
"""Test group creation"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=uid)),
) as group_create,
patch(
"msgraph.generated.groups.item.members.ref.ref_request_builder.RefRequestBuilder.post",
AsyncMock(),
) as member_add,
patch(
"msgraph.generated.groups.item.members.item.ref.ref_request_builder.RefRequestBuilder.delete",
AsyncMock(),
) as member_remove,
):
user = create_test_user(uid)
group = Group.objects.create(name=uid)
group.users.add(user)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
group.users.remove(user)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_called_once()
group_create.assert_called_once()
member_add.assert_called_once()
self.assertEqual(
member_add.call_args[0][0].odata_id,
f"https://graph.microsoft.com/v1.0/directoryObjects/{MicrosoftEntraProviderUser.objects.filter(
provider=self.provider,
).first().microsoft_id}",
)
member_remove.assert_called_once()
def test_group_create_delete_do_nothing(self):
"""Test group deletion (delete action = do nothing)"""
self.provider.group_delete_action = OutgoingSyncDeleteAction.DO_NOTHING
self.provider.save()
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=uid)),
) as group_create,
patch(
"msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.delete",
AsyncMock(return_value=MSGroup(id=uid)),
) as group_delete,
):
group = Group.objects.create(name=uid)
microsoft_group = MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group=group
).first()
self.assertIsNotNone(microsoft_group)
group.delete()
self.assertFalse(
MicrosoftEntraProviderGroup.objects.filter(
provider=self.provider, group__name=uid
).exists()
)
group_create.assert_called_once()
group_delete.assert_not_called()
def test_sync_task(self):
"""Test group discovery"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
different_group = Group.objects.create(
name=uid,
)
self.app.backchannel_providers.add(self.provider)
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.post",
AsyncMock(return_value=MSGroup(id=generate_id())),
),
patch(
"msgraph.generated.groups.item.group_item_request_builder.GroupItemRequestBuilder.patch",
AsyncMock(return_value=MSGroup(id=uid)),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.get",
AsyncMock(
return_value=UserCollectionResponse(
value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)]
)
),
) as user_list,
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get",
AsyncMock(
return_value=GroupCollectionResponse(
value=[MSGroup(display_name=uid, unique_name=uid, id=uid)]
)
),
) as group_list,
):
microsoft_entra_sync.delay(self.provider.pk).get()
self.assertTrue(
MicrosoftEntraProviderGroup.objects.filter(
group=different_group, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()
group_list.assert_called_once()

View File

@ -0,0 +1,373 @@
"""Microsoft Entra User tests"""
from unittest.mock import AsyncMock, MagicMock, patch
from azure.identity.aio import ClientSecretCredential
from django.test import TestCase
from msgraph.generated.models.group_collection_response import GroupCollectionResponse
from msgraph.generated.models.organization import Organization
from msgraph.generated.models.organization_collection_response import OrganizationCollectionResponse
from msgraph.generated.models.user import User as MSUser
from msgraph.generated.models.user_collection_response import UserCollectionResponse
from msgraph.generated.models.verified_domain import VerifiedDomain
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Application, Group, User
from authentik.enterprise.providers.microsoft_entra.models import (
MicrosoftEntraProvider,
MicrosoftEntraProviderMapping,
MicrosoftEntraProviderUser,
)
from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.sync.outgoing.models import OutgoingSyncDeleteAction
from authentik.tenants.models import Tenant
class MicrosoftEntraUserTests(TestCase):
"""Microsoft Entra User tests"""
@apply_blueprint("system/providers-microsoft-entra.yaml")
def setUp(self) -> None:
# Delete all users and groups as the mocked HTTP responses only return one ID
# which will cause errors with multiple users
Tenant.objects.update(avatars="none")
User.objects.all().exclude_anonymous().delete()
Group.objects.all().delete()
self.provider: MicrosoftEntraProvider = MicrosoftEntraProvider.objects.create(
name=generate_id(),
client_id=generate_id(),
client_secret=generate_id(),
tenant_id=generate_id(),
exclude_users_service_account=True,
)
self.app: Application = Application.objects.create(
name=generate_id(),
slug=generate_id(),
)
self.app.backchannel_providers.add(self.provider)
self.provider.property_mappings.add(
MicrosoftEntraProviderMapping.objects.get(
managed="goauthentik.io/providers/microsoft_entra/user"
)
)
self.provider.property_mappings_group.add(
MicrosoftEntraProviderMapping.objects.get(
managed="goauthentik.io/providers/microsoft_entra/group"
)
)
self.creds = ClientSecretCredential(generate_id(), generate_id(), generate_id())
def test_user_create(self):
"""Test user creation"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNotNone(microsoft_user)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_called_once()
def test_user_not_created(self):
"""Test without property mappings, no group is created"""
self.provider.property_mappings.clear()
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNone(microsoft_user)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_not_called()
def test_user_create_update(self):
"""Test user updating"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_patch,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNotNone(microsoft_user)
user.name = "new name"
user.save()
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_called_once()
user_patch.assert_called_once()
def test_user_create_delete(self):
"""Test user deletion"""
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.delete",
AsyncMock(),
) as user_delete,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNotNone(microsoft_user)
user.delete()
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_create.assert_called_once()
user_delete.assert_called_once()
def test_user_create_delete_suspend(self):
"""Test user deletion (delete action = Suspend)"""
self.provider.user_delete_action = OutgoingSyncDeleteAction.SUSPEND
self.provider.save()
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_patch,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.delete",
AsyncMock(),
) as user_delete,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNotNone(microsoft_user)
user.delete()
self.assertFalse(
MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user__username=uid
).exists()
)
user_create.assert_called_once()
user_patch.assert_called_once()
self.assertFalse(user_patch.call_args[0][0].account_enabled)
user_delete.assert_not_called()
def test_user_create_delete_do_nothing(self):
"""Test user deletion (delete action = do nothing)"""
self.provider.user_delete_action = OutgoingSyncDeleteAction.DO_NOTHING
self.provider.save()
uid = generate_id()
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.post",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_create,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
) as user_patch,
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.delete",
AsyncMock(),
) as user_delete,
):
user = User.objects.create(
username=uid,
name=f"{uid} {uid}",
email=f"{uid}@goauthentik.io",
)
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user=user
).first()
self.assertIsNotNone(microsoft_user)
user.delete()
self.assertFalse(
MicrosoftEntraProviderUser.objects.filter(
provider=self.provider, user__username=uid
).exists()
)
user_create.assert_called_once()
user_patch.assert_not_called()
user_delete.assert_not_called()
def test_sync_task(self):
"""Test user discovery"""
uid = generate_id()
self.app.backchannel_providers.remove(self.provider)
different_user = User.objects.create(
username=uid,
email=f"{uid}@goauthentik.io",
)
self.app.backchannel_providers.add(self.provider)
with (
patch(
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
MagicMock(return_value={"credentials": self.creds}),
),
patch(
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
AsyncMock(
return_value=OrganizationCollectionResponse(
value=[
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
]
)
),
),
patch(
"msgraph.generated.users.item.user_item_request_builder.UserItemRequestBuilder.patch",
AsyncMock(return_value=MSUser(id=generate_id())),
),
patch(
"msgraph.generated.users.users_request_builder.UsersRequestBuilder.get",
AsyncMock(
return_value=UserCollectionResponse(
value=[MSUser(mail=f"{uid}@goauthentik.io", id=uid)]
)
),
) as user_list,
patch(
"msgraph.generated.groups.groups_request_builder.GroupsRequestBuilder.get",
AsyncMock(return_value=GroupCollectionResponse(value=[])),
),
):
microsoft_entra_sync.delay(self.provider.pk).get()
self.assertTrue(
MicrosoftEntraProviderUser.objects.filter(
user=different_user, provider=self.provider
).exists()
)
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
user_list.assert_called_once()

View File

@ -0,0 +1,21 @@
"""microsoft provider urls"""
from authentik.enterprise.providers.microsoft_entra.api.groups import (
MicrosoftEntraProviderGroupViewSet,
)
from authentik.enterprise.providers.microsoft_entra.api.property_mappings import (
MicrosoftEntraProviderMappingViewSet,
)
from authentik.enterprise.providers.microsoft_entra.api.providers import (
MicrosoftEntraProviderViewSet,
)
from authentik.enterprise.providers.microsoft_entra.api.users import (
MicrosoftEntraProviderUserViewSet,
)
api_urlpatterns = [
("providers/microsoft_entra", MicrosoftEntraProviderViewSet),
("providers/microsoft_entra_users", MicrosoftEntraProviderUserViewSet),
("providers/microsoft_entra_groups", MicrosoftEntraProviderGroupViewSet),
("propertymappings/provider/microsoft_entra", MicrosoftEntraProviderMappingViewSet),
]

View File

@ -7,7 +7,7 @@ from drf_spectacular.utils import extend_schema_field
from rest_framework.fields import CharField
from rest_framework.viewsets import ModelViewSet
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.api.property_mappings import PropertyMappingSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import JSONDictField
from authentik.enterprise.providers.rac.models import RACPropertyMapping

View File

@ -7,6 +7,7 @@ from deepmerge import always_merger
from django.db import models
from django.db.models import QuerySet
from django.http import HttpRequest
from django.templatetags.static import static
from django.utils.translation import gettext as _
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger
@ -63,6 +64,10 @@ class RACProvider(Provider):
Can return None for providers that are not URL-based"""
return "goauthentik.io://providers/rac/launch"
@property
def icon_url(self) -> str | None:
return static("authentik/sources/rac.svg")
@property
def component(self) -> str:
return "ak-provider-rac-form"

View File

@ -15,6 +15,7 @@ CELERY_BEAT_SCHEDULE = {
TENANT_APPS = [
"authentik.enterprise.audit",
"authentik.enterprise.providers.google_workspace",
"authentik.enterprise.providers.microsoft_entra",
"authentik.enterprise.providers.rac",
"authentik.enterprise.stages.source",
]

View File

@ -18,9 +18,12 @@ class SourceStageSerializer(EnterpriseRequiredMixin, StageSerializer):
source = Source.objects.filter(pk=_source.pk).select_subclasses().first()
if not source:
raise ValidationError("Invalid source")
login_button = source.ui_login_button(self.context["request"])
if not login_button:
raise ValidationError("Invalid source selected, only web-based sources are supported.")
if "request" in self.context:
login_button = source.ui_login_button(self.context["request"])
if not login_button:
raise ValidationError(
"Invalid source selected, only web-based sources are supported."
)
return source
class Meta:

View File

@ -54,7 +54,7 @@ class SourceStageView(ChallengeStageView):
def create_flow_token(self) -> FlowToken:
"""Save the current flow state in a token that can be used to resume this flow"""
pending_user: User = self.get_pending_user()
if pending_user.is_anonymous:
if pending_user.is_anonymous or not pending_user.pk:
pending_user = get_anonymous_user()
current_stage: SourceStage = self.executor.current_stage
identifier = slugify(f"ak-source-stage-{current_stage.name}-{str(uuid4())}")

View File

@ -19,7 +19,8 @@ from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.core.api.utils import PassiveSerializer, TypeCreateSerializer
from authentik.core.api.object_types import TypeCreateSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.events.models import Event, EventAction

View File

@ -45,7 +45,7 @@ class GeoIPContextProcessor(MMDBContextProcessor):
def enrich_context(self, request: HttpRequest) -> dict:
# Different key `geoip` vs `geo` for legacy reasons
return {"geoip": self.city(ClientIPMiddleware.get_client_ip(request))}
return {"geoip": self.city_dict(ClientIPMiddleware.get_client_ip(request))}
def city(self, ip_address: str) -> City | None:
"""Wrapper for Reader.city"""

View File

@ -10,10 +10,10 @@ from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import GenericViewSet
from structlog.stdlib import get_logger
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
from authentik.core.api.utils import MetaNameSerializer
from authentik.core.types import UserSettingSerializer
from authentik.enterprise.apps import EnterpriseConfig
from authentik.flows.api.flows import FlowSetSerializer
from authentik.flows.models import ConfigurableStage, Stage
from authentik.lib.utils.reflection import all_subclasses
@ -47,6 +47,7 @@ class StageSerializer(ModelSerializer, MetaNameSerializer):
class StageViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -63,25 +64,6 @@ class StageViewSet(
def get_queryset(self): # pragma: no cover
return Stage.objects.select_subclasses().prefetch_related("flow_set")
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable stage types"""
data = []
for subclass in all_subclasses(self.queryset.model, False):
subclass: Stage
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
"requires_enterprise": isinstance(subclass._meta.app_config, EnterpriseConfig),
}
)
data = sorted(data, key=lambda x: x["name"])
return Response(TypeCreateSerializer(data, many=True).data)
@extend_schema(responses={200: UserSettingSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def user_settings(self, request: Request) -> Response:

View File

@ -203,7 +203,8 @@ class FlowPlanner:
"f(plan): building plan",
)
plan = self._build_plan(user, request, default_context)
cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
if self.use_cache:
cache.set(cache_key(self.flow, user), plan, CACHE_TIMEOUT)
if not plan.bindings and not self.allow_empty_flows:
raise EmptyFlowException()
return plan

View File

@ -2,7 +2,7 @@
from base64 import b64encode
from functools import cache as funccache
from hashlib import md5
from hashlib import md5, sha256
from typing import TYPE_CHECKING
from urllib.parse import urlencode
@ -20,7 +20,7 @@ from authentik.tenants.utils import get_current_tenant
if TYPE_CHECKING:
from authentik.core.models import User
GRAVATAR_URL = "https://secure.gravatar.com"
GRAVATAR_URL = "https://www.gravatar.com"
DEFAULT_AVATAR = static("dist/assets/images/user_default.png")
CACHE_KEY_GRAVATAR = "goauthentik.io/lib/avatars/"
CACHE_KEY_GRAVATAR_AVAILABLE = "goauthentik.io/lib/avatars/gravatar_available"
@ -55,10 +55,9 @@ def avatar_mode_gravatar(user: "User", mode: str) -> str | None:
if not cache.get(CACHE_KEY_GRAVATAR_AVAILABLE, True):
return None
# gravatar uses md5 for their URLs, so md5 can't be avoided
mail_hash = md5(user.email.lower().encode("utf-8")).hexdigest() # nosec
parameters = [("size", "158"), ("rating", "g"), ("default", "404")]
gravatar_url = f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters, doseq=True)}"
mail_hash = sha256(user.email.lower().encode("utf-8")).hexdigest() # nosec
parameters = {"size": "158", "rating": "g", "default": "404"}
gravatar_url = f"{GRAVATAR_URL}/avatar/{mail_hash}?{urlencode(parameters)}"
full_key = CACHE_KEY_GRAVATAR + mail_hash
if cache.has_key(full_key):
@ -84,7 +83,9 @@ def avatar_mode_gravatar(user: "User", mode: str) -> str | None:
def generate_colors(text: str) -> tuple[str, str]:
"""Generate colours based on `text`"""
color = int(md5(text.lower().encode("utf-8")).hexdigest(), 16) % 0xFFFFFF # nosec
color = (
int(md5(text.lower().encode("utf-8"), usedforsecurity=False).hexdigest(), 16) % 0xFFFFFF
) # nosec
# Get a (somewhat arbitrarily) reduced scope of colors
# to avoid too dark or light backgrounds
@ -179,7 +180,7 @@ def avatar_mode_generated(user: "User", mode: str) -> str | None:
def avatar_mode_url(user: "User", mode: str) -> str | None:
"""Format url"""
mail_hash = md5(user.email.lower().encode("utf-8")).hexdigest() # nosec
mail_hash = md5(user.email.lower().encode("utf-8"), usedforsecurity=False).hexdigest() # nosec
return mode % {
"username": user.username,
"mail_hash": mail_hash,

View File

@ -304,6 +304,12 @@ class ConfigLoader:
"""Wrapper for get that converts value into boolean"""
return str(self.get(path, default)).lower() == "true"
def get_keys(self, path: str, sep=".") -> list[str]:
"""List attribute keys by using yaml path"""
root = self.raw
attr: Attr = get_path_from_dict(root, path, sep=sep, default=Attr({}))
return attr.keys()
def get_dict_from_b64_json(self, path: str, default=None) -> dict:
"""Wrapper for get that converts value from Base64 encoded string into dictionary"""
config_value = self.get(path)

View File

@ -10,6 +10,10 @@ postgresql:
use_pgpool: false
test:
name: test_authentik
read_replicas: {}
# For example
# 0:
# host: replica1.example.com
listen:
listen_http: 0.0.0.0:9000

View File

@ -5,6 +5,7 @@ import socket
from collections.abc import Iterable
from ipaddress import ip_address, ip_network
from textwrap import indent
from types import CodeType
from typing import Any
from cachetools import TLRUCache, cached
@ -184,7 +185,7 @@ class BaseEvaluator:
full_expression += f"\nresult = handler({handler_signature})"
return full_expression
def compile(self, expression: str) -> Any:
def compile(self, expression: str) -> CodeType:
"""Parse expression. Raises SyntaxError or ValueError if the syntax is incorrect."""
param_keys = self._context.keys()
return compile(self.wrap_expression(expression, param_keys), self._filename, "exec")

View File

@ -101,6 +101,9 @@ def get_logger_config():
"uvicorn": "WARNING",
"gunicorn": "INFO",
"requests_mock": "WARNING",
"hpack": "WARNING",
"httpx": "WARNING",
"azure": "WARNING",
}
for handler_name, level in handler_level_map.items():
base_config["loggers"][handler_name] = {

View File

@ -0,0 +1,69 @@
from collections.abc import Generator
from django.db.models import QuerySet
from django.http import HttpRequest
from authentik.core.expression.evaluator import PropertyMappingEvaluator
from authentik.core.expression.exceptions import PropertyMappingExpressionException
from authentik.core.models import PropertyMapping, User
class PropertyMappingManager:
"""Pre-compile and cache property mappings when an identical
set is used multiple times"""
query_set: QuerySet[PropertyMapping]
mapping_subclass: type[PropertyMapping]
_evaluators: list[PropertyMappingEvaluator]
def __init__(
self,
qs: QuerySet[PropertyMapping],
# Expected subclass of PropertyMappings, any objects in the queryset
# that are not an instance of this class will be discarded
mapping_subclass: type[PropertyMapping],
# As they keys of parameters are part of the compilation,
# we need a list of all parameter names that will be used during evaluation
context_keys: list[str],
) -> None:
self.query_set = qs
self.mapping_subclass = mapping_subclass
self.context_keys = context_keys
self.compile()
def compile(self):
self._evaluators = []
for mapping in self.query_set:
if not isinstance(mapping, self.mapping_subclass):
continue
evaluator = PropertyMappingEvaluator(
mapping, **{key: None for key in self.context_keys}
)
# Compile and cache expression
evaluator.compile()
self._evaluators.append(evaluator)
def iter_eval(
self,
user: User | None,
request: HttpRequest | None,
return_mapping: bool = False,
**kwargs,
) -> Generator[tuple[dict, PropertyMapping], None]:
"""Iterate over all mappings that were pre-compiled and
execute all of them with the given context"""
for mapping in self._evaluators:
mapping.set_context(user, request, **kwargs)
try:
value = mapping.evaluate(mapping.model.expression)
except PropertyMappingExpressionException as exc:
raise exc from exc
except Exception as exc:
raise PropertyMappingExpressionException(exc, mapping.model) from exc
if value is None:
continue
if return_mapping:
yield value, mapping.model
else:
yield value

View File

@ -3,3 +3,6 @@
PAGE_SIZE = 100
PAGE_TIMEOUT = 60 * 60 * 0.5 # Half an hour
HTTP_CONFLICT = 409
HTTP_NO_CONTENT = 204
HTTP_SERVICE_UNAVAILABLE = 503
HTTP_TOO_MANY_REQUESTS = 429

View File

@ -47,8 +47,10 @@ class OutgoingSyncProviderStatusMixin:
uid=slugify(provider.name),
)
)
status = {
"tasks": tasks,
"is_running": provider.sync_lock.locked(),
}
with provider.sync_lock as lock_acquired:
status = {
"tasks": tasks,
# If we could not acquire the lock, it means a task is using it, and thus is running
"is_running": not lock_acquired,
}
return Response(SyncStatusSerializer(status).data)

View File

@ -3,10 +3,18 @@
from enum import StrEnum
from typing import TYPE_CHECKING
from deepmerge import always_merger
from django.db import DatabaseError
from structlog.stdlib import get_logger
from authentik.lib.sync.outgoing.exceptions import NotFoundSyncException
from authentik.core.expression.exceptions import (
PropertyMappingExpressionException,
SkipObjectException,
)
from authentik.events.models import Event, EventAction
from authentik.lib.sync.mapper import PropertyMappingManager
from authentik.lib.sync.outgoing.exceptions import NotFoundSyncException, StopSync
from authentik.lib.utils.errors import exception_to_string
if TYPE_CHECKING:
from django.db.models import Model
@ -28,6 +36,7 @@ class BaseOutgoingSyncClient[
provider: TProvider
connection_type: type[TConnection]
connection_type_query: str
mapper: PropertyMappingManager
can_discover = False
@ -39,26 +48,25 @@ class BaseOutgoingSyncClient[
"""Create object in remote destination"""
raise NotImplementedError()
def update(self, obj: TModel, connection: object):
def update(self, obj: TModel, connection: TConnection):
"""Update object in remote destination"""
raise NotImplementedError()
def write(self, obj: TModel) -> tuple[TConnection, bool]:
"""Write object to destination. Uses self.create and self.update, but
can be overwritten for further logic"""
remote_obj = self.connection_type.objects.filter(
connection = self.connection_type.objects.filter(
provider=self.provider, **{self.connection_type_query: obj}
).first()
connection: TConnection | None = None
try:
if not remote_obj:
if not connection:
connection = self.create(obj)
return connection, True
try:
self.update(obj, remote_obj)
return remote_obj, False
self.update(obj, connection)
return connection, False
except NotFoundSyncException:
remote_obj.delete()
connection.delete()
connection = self.create(obj)
return connection, True
except DatabaseError as exc:
@ -71,9 +79,34 @@ class BaseOutgoingSyncClient[
"""Delete object from destination"""
raise NotImplementedError()
def to_schema(self, obj: TModel) -> TSchema:
def to_schema(self, obj: TModel, connection: TConnection | None, **defaults) -> TSchema:
"""Convert object to destination schema"""
raise NotImplementedError()
raw_final_object = {}
try:
eval_kwargs = {
"request": None,
"provider": self.provider,
"connection": connection,
obj._meta.model_name: obj,
}
eval_kwargs.setdefault("user", None)
for value in self.mapper.iter_eval(**eval_kwargs):
always_merger.merge(raw_final_object, value)
except SkipObjectException as exc:
raise exc from exc
except PropertyMappingExpressionException as exc:
# Value error can be raised when assigning invalid data to an attribute
Event.new(
EventAction.CONFIGURATION_ERROR,
message=f"Failed to evaluate property-mapping {exception_to_string(exc)}",
mapping=exc.mapping,
).save()
raise StopSync(exc, obj, exc.mapping) from exc
if not raw_final_object:
raise StopSync(ValueError("No mappings configured"), obj)
for key, value in defaults.items():
raw_final_object.setdefault(key, value)
return raw_final_object
def discover(self):
"""Optional method. Can be used to implement a "discovery" where

View File

@ -17,6 +17,10 @@ class ObjectExistsSyncException(BaseSyncException):
"""Exception when an object already exists in the remote system"""
class BadRequestSyncException(BaseSyncException):
"""Exception when invalid data was sent to the remote system"""
class StopSync(BaseSyncException):
"""Exception raised when a configuration error should stop the sync process"""

View File

@ -1,14 +1,22 @@
from typing import Any, Self
from django.core.cache import cache
from django.db.models import Model, QuerySet
from redis.lock import Lock
import pglock
from django.db import connection
from django.db.models import Model, QuerySet, TextChoices
from authentik.core.models import Group, User
from authentik.lib.sync.outgoing import PAGE_TIMEOUT
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
class OutgoingSyncDeleteAction(TextChoices):
"""Action taken when a user/group is deleted in authentik. Suspend is not available for groups,
and will be treated as `do_nothing`"""
DO_NOTHING = "do_nothing"
DELETE = "delete"
SUSPEND = "suspend"
class OutgoingSyncProvider(Model):
class Meta:
@ -23,10 +31,10 @@ class OutgoingSyncProvider(Model):
raise NotImplementedError
@property
def sync_lock(self) -> Lock:
"""Redis lock to prevent multiple parallel syncs happening"""
return Lock(
cache.client.get_client(),
name=f"goauthentik.io/providers/outgoing-sync/{str(self.pk)}",
timeout=(60 * 60 * PAGE_TIMEOUT) * 3,
def sync_lock(self) -> pglock.advisory:
"""Postgres lock for syncing SCIM to prevent multiple parallel syncs happening"""
return pglock.advisory(
lock_id=f"goauthentik.io/{connection.schema_name}/providers/outgoing-sync/{str(self.pk)}",
timeout=0,
side_effect=pglock.Return,
)

View File

@ -47,7 +47,7 @@ def register_signals(
return
task_sync_direct.delay(
class_to_path(instance.__class__), instance.pk, Direction.remove.value
)
).get(propagate=False)
pre_delete.connect(model_pre_delete, User, dispatch_uid=uid, weak=False)
pre_delete.connect(model_pre_delete, Group, dispatch_uid=uid, weak=False)

View File

@ -1,5 +1,7 @@
from collections.abc import Callable
from dataclasses import asdict
from celery.exceptions import Retry
from celery.result import allow_join_result
from django.core.paginator import Paginator
from django.db.models import Model, QuerySet
@ -12,9 +14,14 @@ from authentik.core.models import Group, User
from authentik.events.logs import LogEvent
from authentik.events.models import TaskStatus
from authentik.events.system_tasks import SystemTask
from authentik.events.utils import sanitize_item
from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
from authentik.lib.sync.outgoing.base import Direction
from authentik.lib.sync.outgoing.exceptions import StopSync, TransientSyncException
from authentik.lib.sync.outgoing.exceptions import (
BadRequestSyncException,
StopSync,
TransientSyncException,
)
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
from authentik.lib.utils.reflection import class_to_path, path_to_class
@ -59,17 +66,16 @@ class SyncTasks:
).first()
if not provider:
return
lock = provider.sync_lock
if lock.locked():
self.logger.debug("Sync locked, skipping task", source=provider.name)
return
task.set_uid(slugify(provider.name))
messages = []
messages.append(_("Starting full provider sync"))
self.logger.debug("Starting provider sync")
users_paginator = Paginator(provider.get_object_qs(User), PAGE_SIZE)
groups_paginator = Paginator(provider.get_object_qs(Group), PAGE_SIZE)
with allow_join_result(), lock:
with allow_join_result(), provider.sync_lock as lock_acquired:
if not lock_acquired:
self.logger.debug("Failed to acquire sync lock, skipping", provider=provider.name)
return
try:
for page in users_paginator.page_range:
messages.append(_("Syncing page %(page)d of users" % {"page": page}))
@ -78,7 +84,7 @@ class SyncTasks:
time_limit=PAGE_TIMEOUT,
soft_time_limit=PAGE_TIMEOUT,
).get():
messages.append(msg)
messages.append(LogEvent(**msg))
for page in groups_paginator.page_range:
messages.append(_("Syncing page %(page)d of groups" % {"page": page}))
for msg in sync_objects.apply_async(
@ -86,7 +92,7 @@ class SyncTasks:
time_limit=PAGE_TIMEOUT,
soft_time_limit=PAGE_TIMEOUT,
).get():
messages.append(msg)
messages.append(LogEvent(**msg))
except TransientSyncException as exc:
self.logger.warning("transient sync exception", exc=exc)
raise task.retry(exc=exc) from exc
@ -120,40 +126,70 @@ class SyncTasks:
try:
client.write(obj)
except SkipObjectException:
self.logger.debug("skipping object due to SkipObject", obj=obj)
continue
except BadRequestSyncException as exc:
self.logger.warning("failed to sync object", exc=exc, obj=obj)
messages.append(
asdict(
LogEvent(
_(
(
"Failed to sync {object_type} {object_name} "
"due to error: {error}"
).format_map(
{
"object_type": obj._meta.verbose_name,
"object_name": str(obj),
"error": str(exc),
}
)
),
log_level="warning",
logger=f"{provider._meta.verbose_name}@{object_type}",
attributes={"arguments": exc.args[1:], "obj": sanitize_item(obj)},
)
)
)
except TransientSyncException as exc:
self.logger.warning("failed to sync object", exc=exc, user=obj)
messages.append(
LogEvent(
_(
(
"Failed to sync {object_type} {object_name} "
"due to transient error: {error}"
).format_map(
{
"object_type": obj._meta.verbose_name,
"object_name": str(obj),
"error": str(exc),
}
)
),
log_level="warning",
logger="",
asdict(
LogEvent(
_(
(
"Failed to sync {object_type} {object_name} "
"due to transient error: {error}"
).format_map(
{
"object_type": obj._meta.verbose_name,
"object_name": str(obj),
"error": str(exc),
}
)
),
log_level="warning",
logger=f"{provider._meta.verbose_name}@{object_type}",
attributes={"obj": sanitize_item(obj)},
)
)
)
except StopSync as exc:
self.logger.warning("Stopping sync", exc=exc)
messages.append(
LogEvent(
_(
"Stopping sync due to error: {error}".format_map(
{
"error": exc.detail(),
}
)
),
log_level="warning",
logger="",
asdict(
LogEvent(
_(
"Stopping sync due to error: {error}".format_map(
{
"error": exc.detail(),
}
)
),
log_level="warning",
logger=f"{provider._meta.verbose_name}@{object_type}",
attributes={"obj": sanitize_item(obj)},
)
)
)
break
@ -185,7 +221,9 @@ class SyncTasks:
client.write(instance)
if operation == Direction.remove:
client.delete(instance)
except (StopSync, TransientSyncException) as exc:
except TransientSyncException as exc:
raise Retry() from exc
except StopSync as exc:
self.logger.warning(exc, provider_pk=provider.pk)
def sync_signal_m2m(self, group_pk: str, action: str, pk_set: list[int]):
@ -211,5 +249,7 @@ class SyncTasks:
if action == "post_remove":
operation = Direction.remove
client.update_group(group, operation, pk_set)
except (StopSync, TransientSyncException) as exc:
except TransientSyncException as exc:
raise Retry() from exc
except StopSync as exc:
self.logger.warning(exc, provider_pk=provider.pk)

View File

@ -169,3 +169,9 @@ class TestConfig(TestCase):
self.assertEqual(config.get("cache.timeout_flows"), "32m")
self.assertEqual(config.get("cache.timeout_policies"), "3920ns")
self.assertEqual(config.get("cache.timeout_reputation"), "298382us")
def test_get_keys(self):
"""Test get_keys"""
config = ConfigLoader()
config.set("foo.bar", "baz")
self.assertEqual(list(config.get_keys("foo")), ["bar"])

View File

@ -12,7 +12,7 @@ from authentik.lib.config import CONFIG
SERVICE_HOST_ENV_NAME = "KUBERNETES_SERVICE_HOST"
def all_subclasses(cls, sort=True):
def all_subclasses[T](cls: T, sort=True) -> list[T] | set[T]:
"""Recursively return all subclassess of cls"""
classes = set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c, sort=sort)]

View File

@ -117,8 +117,12 @@ class OutpostHealthSerializer(PassiveSerializer):
uid = CharField(read_only=True)
last_seen = DateTimeField(read_only=True)
version = CharField(read_only=True)
version_should = CharField(read_only=True)
golang_version = CharField(read_only=True)
openssl_enabled = BooleanField(read_only=True)
openssl_version = CharField(read_only=True)
fips_enabled = BooleanField(read_only=True)
version_should = CharField(read_only=True)
version_outdated = BooleanField(read_only=True)
build_hash = CharField(read_only=True, required=False)
@ -173,6 +177,10 @@ class OutpostViewSet(UsedByMixin, ModelViewSet):
"version_should": state.version_should,
"version_outdated": state.version_outdated,
"build_hash": state.build_hash,
"golang_version": state.golang_version,
"openssl_enabled": state.openssl_enabled,
"openssl_version": state.openssl_version,
"fips_enabled": state.fips_enabled,
"hostname": state.hostname,
"build_hash_should": get_build_hash(),
}

View File

@ -15,9 +15,12 @@ from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
from authentik.lib.utils.reflection import all_subclasses
from authentik.core.api.utils import (
MetaNameSerializer,
PassiveSerializer,
)
from authentik.outposts.models import (
DockerServiceConnection,
KubernetesServiceConnection,
@ -57,6 +60,7 @@ class ServiceConnectionStateSerializer(PassiveSerializer):
class ServiceConnectionViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -70,23 +74,6 @@ class ServiceConnectionViewSet(
search_fields = ["name"]
filterset_fields = ["name"]
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable service connection types"""
data = []
for subclass in all_subclasses(self.queryset.model):
subclass: OutpostServiceConnection
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@extend_schema(responses={200: ServiceConnectionStateSerializer(many=False)})
@action(detail=True, pagination_class=None, filter_backends=[])
def state(self, request: Request, pk: str) -> Response:

View File

@ -121,6 +121,10 @@ class OutpostConsumer(JsonWebsocketConsumer):
if msg.instruction == WebsocketMessageInstruction.HELLO:
state.version = msg.args.pop("version", None)
state.build_hash = msg.args.pop("buildHash", "")
state.golang_version = msg.args.pop("golangVersion", "")
state.openssl_enabled = msg.args.pop("opensslEnabled", False)
state.openssl_version = msg.args.pop("opensslVersion", "")
state.fips_enabled = msg.args.pop("fipsEnabled", False)
state.args.update(msg.args)
elif msg.instruction == WebsocketMessageInstruction.ACK:
return

View File

@ -124,7 +124,6 @@ class KubernetesObjectReconciler(Generic[T]):
self.update(current, reference)
self.logger.debug("Updating")
except (OpenApiException, HTTPError) as exc:
if isinstance(exc, ApiException) and exc.status == 422: # noqa: PLR2004
self.logger.debug("Failed to update current, triggering re-create")
self._recreate(current=current, reference=reference)

View File

@ -131,7 +131,7 @@ class OutpostServiceConnection(models.Model):
verbose_name = _("Outpost Service-Connection")
verbose_name_plural = _("Outpost Service-Connections")
def __str__(self) -> __version__:
def __str__(self) -> str:
return f"Outpost service connection {self.name}"
@property
@ -434,6 +434,10 @@ class OutpostState:
version: str | None = field(default=None)
version_should: Version = field(default=OUR_VERSION)
build_hash: str = field(default="")
golang_version: str = field(default="")
openssl_enabled: bool = field(default=False)
openssl_version: str = field(default="")
fips_enabled: bool = field(default=False)
hostname: str = field(default="")
args: dict = field(default_factory=dict)

View File

@ -13,10 +13,13 @@ from rest_framework.viewsets import GenericViewSet
from structlog.stdlib import get_logger
from authentik.core.api.applications import user_app_cache_key
from authentik.core.api.object_types import TypesMixin
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import CacheSerializer, MetaNameSerializer, TypeCreateSerializer
from authentik.core.api.utils import (
CacheSerializer,
MetaNameSerializer,
)
from authentik.events.logs import LogEventSerializer, capture_logs
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer
from authentik.policies.models import Policy, PolicyBinding
from authentik.policies.process import PolicyProcess
@ -69,6 +72,7 @@ class PolicySerializer(ModelSerializer, MetaNameSerializer):
class PolicyViewSet(
TypesMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
UsedByMixin,
@ -89,23 +93,6 @@ class PolicyViewSet(
def get_queryset(self): # pragma: no cover
return Policy.objects.select_subclasses().prefetch_related("bindings", "promptstage_set")
@extend_schema(responses={200: TypeCreateSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def types(self, request: Request) -> Response:
"""Get all creatable policy types"""
data = []
for subclass in all_subclasses(self.queryset.model):
subclass: Policy
data.append(
{
"name": subclass._meta.verbose_name,
"description": subclass.__doc__,
"component": subclass().component,
"model_name": subclass._meta.model_name,
}
)
return Response(TypeCreateSerializer(data, many=True).data)
@permission_required(None, ["authentik_policies.view_policy_cache"])
@extend_schema(responses={200: CacheSerializer(many=False)})
@action(detail=False, pagination_class=None, filter_backends=[])

View File

@ -102,7 +102,7 @@ class EventMatcherPolicy(Policy):
result = checker(request, event)
if result is None:
continue
LOGGER.info(
LOGGER.debug(
"Event matcher check result",
checker=checker.__name__,
result=result,

View File

@ -96,16 +96,42 @@ class TestEvaluator(TestCase):
execution_logging=True,
expression="ak_message(request.http_request.path)\nreturn True",
)
tmpl = f"""
ak_message(request.http_request.path)
res = ak_call_policy('{expr.name}')
ak_message(request.http_request.path)
for msg in res.messages:
ak_message(msg)
"""
evaluator = PolicyEvaluator("test")
evaluator.set_policy_request(self.request)
res = evaluator.evaluate(tmpl)
expr2 = ExpressionPolicy.objects.create(
name=generate_id(),
execution_logging=True,
expression=f"""
ak_message(request.http_request.path)
res = ak_call_policy('{expr.name}')
ak_message(request.http_request.path)
for msg in res.messages:
ak_message(msg)
""",
)
proc = PolicyProcess(PolicyBinding(policy=expr2), request=self.request, connection=None)
res = proc.profiling_wrapper()
self.assertEqual(res.messages, ("/", "/", "/"))
def test_call_policy_test_like(self):
"""test ak_call_policy without `obj` set, as if it was when testing policies"""
expr = ExpressionPolicy.objects.create(
name=generate_id(),
execution_logging=True,
expression="ak_message(request.http_request.path)\nreturn True",
)
expr2 = ExpressionPolicy.objects.create(
name=generate_id(),
execution_logging=True,
expression=f"""
ak_message(request.http_request.path)
res = ak_call_policy('{expr.name}')
ak_message(request.http_request.path)
for msg in res.messages:
ak_message(msg)
""",
)
self.request.obj = None
proc = PolicyProcess(PolicyBinding(policy=expr2), request=self.request, connection=None)
res = proc.profiling_wrapper()
self.assertEqual(res.messages, ("/", "/", "/"))

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