Compare commits

..

168 Commits

Author SHA1 Message Date
87bf75e51c add Registration closed note 2023-07-28 12:36:15 -05:00
1ba1a1def5 web/user: fix app icon size for user interface
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 17:11:20 +02:00
782d95b4a3 web: app icons v2 (#6410)
* fix more icons stuff

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

* refactor app icon into separate component

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

* update locale

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

* make app icon work correctly in admin list and app view page

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 14:25:56 +02:00
5803c39e91 web: fix app icon rendering, style refinements (#6409)
* add very slight drop shadow to icons so dark colours are better visible, fix expand text

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

* web/admin: fix rendering of icons for admin interface

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 11:09:11 +02:00
e5322a6dd3 core: bump goauthentik.io/api/v3 from 3.2023061.5 to 3.2023061.6 (#6407)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.5 to 3.2023061.6.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.5...v3.2023061.6)

---
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>
2023-07-28 11:08:57 +02:00
364edfb4a8 web: bump core-js from 3.31.1 to 3.32.0 in /web (#6406)
Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.31.1 to 3.32.0.
- [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.32.0/packages/core-js)

---
updated-dependencies:
- dependency-name: core-js
  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>
2023-07-28 11:08:47 +02:00
de16988cac web/user: experiment with some slightly different styles (#6405)
* web/user: experiment with some slightly different styles

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

* rework application card

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

* fix color and expand

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

* fix expansion

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-28 00:23:06 +02:00
a2714ab1f1 outposts: make metrics compliant with Prometheus best-practices (#6398)
web/outpost: make metrics compliant with Prometheus best-practices

Today, all NewHistogramVec store values in nanoseconds without changing
the default histogram bucket, which are made for seconds, making them
a bit useless. In addition, some metrics names are not self-explanatoryand
and do not comply with Prometheus best practices.

This commit tries to fix all of this "issues".

NOTE: I kept old metrics in order to avoid breaking changes with
existing dashboards and metrics.

Signed-off-by: Alexandre NICOLAIE <xunleii@users.noreply.github.com>
2023-07-27 18:51:08 +02:00
5347dd7022 website: add tooltips to comparison table (#6402)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 17:08:11 +02:00
aaddb76962 web: bump API Client version (#6401) 2023-07-27 13:14:02 +02:00
b08f8d8e0c api: re-fix url import logging (#6400)
* fix logging

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

* remove lib from apps

lib doesn't declare any models, so it really doesn't need to be in there anyways?

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

* remove lib from schema too

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 12:56:51 +02:00
664bc19bba website: revamp (#6375)
* make things work better

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

* fix styling

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

* move comparison css to its own file

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

* more cleanup

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

* more cleanup

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

* make release bar work, more cleanup

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

* fix a bunch of styling issues

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

* fix table

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

* move text slider into component

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

* fix mobile and more cleanup

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

* more fixes

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

* test out gradient?

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

* update meta?

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

* fix lint

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

* remove underline on news links

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

* adjust gradient

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

* remove override

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

* start changing screenshots

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

* use smaller screenshots for landing page

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

* website: fix the font scaling issue on the text slider

- Change the text slider and hero__title to use em/rem calculations

This patch changes the way the text slider and hero__title font sizes
are calculated so that the font scales with the device; devices with
viewports larger than 379 pixels will now render the slider without
line breaks or having the phrase "active directory" disappear from
the page.

The 379pixel break is just the best we could come up with on the fly.
This does mean that if you own an iPhone 5 or an old WIFI-capable
iPod, it still looks awful.  :-)

* fix some more react issues

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

* a bit less padding on the bottom CTA

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

* use some old copy for now

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
2023-07-27 11:44:50 +02:00
f315360be1 web: bump @sentry/browser from 7.60.0 to 7.60.1 in /web (#6392)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.60.0 to 7.60.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.60.0...7.60.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-27 11:32:57 +02:00
4ac255d579 web: bump @sentry/tracing from 7.60.0 to 7.60.1 in /web (#6393)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.60.0 to 7.60.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.60.0...7.60.1)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-27 11:30:15 +02:00
3f9f57f0fd web: bump chart.js from 4.3.1 to 4.3.2 in /web (#6395)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.3.1 to 4.3.2.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.3.1...v4.3.2)

---
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>
2023-07-27 11:30:04 +02:00
3569eb15b1 core: bump pylint from 2.17.4 to 2.17.5 (#6396)
Bumps [pylint](https://github.com/pylint-dev/pylint) from 2.17.4 to 2.17.5.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v2.17.4...v2.17.5)

---
updated-dependencies:
- dependency-name: pylint
  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>
2023-07-27 11:29:52 +02:00
94836a3ce7 api: log errors if app URLs import fail (#6397)
* api: log errors if app URLs import fail

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

* bump level to warning

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

---------

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-27 11:29:20 +02:00
f272d14fcf events: fix monitored task not removing state (#6386)
when `save_on_success` is set, a task failure saves state. when it succeeds afterwards, that state should be removed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 16:00:50 +02:00
17fe595528 sources/ldap: fix syncing large LDAP directories (#6384)
* sources/ldap: fix syncing large LDAP directories

* add test

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 12:25:40 +02:00
3cce6d79eb web/user: fix background alignment (#6383)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:03:58 +02:00
7ac5c8eaa6 translate: Updates for file locale/en/LC_MESSAGES/django.po in fr on branch main (#6376)
* Translate locale/en/LC_MESSAGES/django.po in fr

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

* remove debug

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:03:37 +02:00
7316f126de ci: test rename action more
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 11:02:02 +02:00
d645965a33 web: bump mermaid from 10.2.4 to 10.3.0 in /web (#6382)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.2.4 to 10.3.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/v10.2.4...v10.3.0)

---
updated-dependencies:
- dependency-name: mermaid
  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>
2023-07-26 11:00:16 +02:00
47abbcf8b8 ci: test rename
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 10:59:59 +02:00
e86a41b83d ci: automatically rename transifex PRs to match the naming scheme (#6352)
* ci: automatically rename transifex PRs to match the naming scheme

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

* add name

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-26 10:57:08 +02:00
f2293c0f5b website/docs: Update syntax in traefik standalone example (#6303)
* Update syntax in traefik standalone example

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>

* One more syntax update

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>

---------

Signed-off-by: Thomas Moschny <thomas.moschny@gmx.de>
2023-07-26 10:56:31 +02:00
da3393abb4 web: bump @esbuild/linux-arm64 from 0.18.16 to 0.18.17 in /web (#6380)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.16 to 0.18.17.
- [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.18.16...v0.18.17)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-26 10:54:40 +02:00
211da35a93 web: bump pyright from 1.1.318 to 1.1.319 in /web (#6378)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.318 to 1.1.319.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.319/packages/pyright)

---
updated-dependencies:
- dependency-name: pyright
  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>
2023-07-26 10:47:26 +02:00
0b8c501326 web: bump @esbuild/darwin-arm64 from 0.18.16 to 0.18.17 in /web (#6379)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.16 to 0.18.17.
- [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.18.16...v0.18.17)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  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>
2023-07-26 10:47:09 +02:00
18472c231a enterprise: fix license check not using the proper JWT algorithm
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2023-07-25 12:10:15 +02:00
e51bef218a web: bump @typescript-eslint/parser from 6.1.0 to 6.2.0 in /web (#6372)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.2.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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>
2023-07-25 11:43:08 +02:00
486e17920e core: bump goauthentik.io/api/v3 from 3.2023061.4 to 3.2023061.5 (#6362)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.4 to 3.2023061.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.4...v3.2023061.5)

---
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>
2023-07-25 11:34:24 +02:00
505bad0895 web: bump @typescript-eslint/eslint-plugin from 6.1.0 to 6.2.0 in /web (#6370)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.2.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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>
2023-07-25 11:33:04 +02:00
e4b7691181 web: bump tslib from 2.6.0 to 2.6.1 in /web (#6366)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/2.6.0...v2.6.1)

---
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>
2023-07-25 11:32:48 +02:00
ba5adad53d web: bump @storybook/addon-links from 7.1.0 to 7.1.1 in /web (#6363)
Bumps [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) from 7.1.0 to 7.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/v7.1.1/code/addons/links)

---
updated-dependencies:
- dependency-name: "@storybook/addon-links"
  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>
2023-07-25 11:31:48 +02:00
2b1dee6aed web: bump storybook from 7.1.0 to 7.1.1 in /web (#6364)
Bumps [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) from 7.1.0 to 7.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/v7.1.1/code/lib/cli)

---
updated-dependencies:
- dependency-name: storybook
  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>
2023-07-25 11:28:38 +02:00
b976acff42 web: bump chart.js from 4.3.0 to 4.3.1 in /web (#6368)
Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/chartjs/Chart.js/releases)
- [Commits](https://github.com/chartjs/Chart.js/compare/v4.3.0...v4.3.1)

---
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>
2023-07-25 11:28:20 +02:00
78092ddfea web: bump @storybook/addon-essentials from 7.1.0 to 7.1.1 in /web (#6365)
Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) from 7.1.0 to 7.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/v7.1.1/code/addons/essentials)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  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>
2023-07-25 11:27:50 +02:00
22d013817f web: bump @storybook/web-components-vite from 7.1.0 to 7.1.1 in /web (#6367)
Bumps [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) from 7.1.0 to 7.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/v7.1.1/code/frameworks/web-components-vite)

---
updated-dependencies:
- dependency-name: "@storybook/web-components-vite"
  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>
2023-07-25 11:12:14 +02:00
56224fc712 web: bump @storybook/blocks from 7.1.0 to 7.1.1 in /web (#6371)
Bumps [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/blocks) from 7.1.0 to 7.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/v7.1.1/code/ui/blocks)

---
updated-dependencies:
- dependency-name: "@storybook/blocks"
  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>
2023-07-25 11:11:01 +02:00
86d64b2234 web/admin: hide pagination when no data is loaded yet (#6353)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:59:43 +02:00
a320aec9d0 web/admin: adjust style of page header (#6355)
light theme now matches dark theme

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:59:09 +02:00
7be94df00c root: set csrf cookie's secure flag same as session (#6350)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 13:57:30 +02:00
346c6e6a85 outposts: Fix infinite self-recursion in traefik reconciler. (#6336)
Fix infinite self-recursion in traefik reconciler.
2023-07-24 10:25:29 +00:00
8d4b7ce8d3 outposts: fix patch processing (#6338)
* outposts: fix patch processing for custom object types

* outposts: correct parsing patch type

* small change

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 10:25:14 +00:00
56cf14e5ef web: bump API Client version (#6351) 2023-07-24 12:23:20 +02:00
69543c14d3 Updates for file web/xliff/en.xlf in zh_CN on branch main (#6340)
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>
2023-07-24 12:13:31 +02:00
f3f07f2c98 Updates for file web/xliff/en.xlf in zh-Hans on branch main (#6341)
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>
2023-07-24 12:13:21 +02:00
4647fbacb0 enterprise: fix license check not using DER as spec specifies (#6348)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-24 12:11:47 +02:00
4359fab560 core: bump goauthentik.io/api/v3 from 3.2023061.3 to 3.2023061.4 (#6342)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.3 to 3.2023061.4.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.3...v3.2023061.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>
2023-07-24 11:51:05 +02:00
f8b36e1737 core: bump drf-spectacular from 0.26.3 to 0.26.4 (#6343)
Bumps [drf-spectacular](https://github.com/tfranzel/drf-spectacular) from 0.26.3 to 0.26.4.
- [Release notes](https://github.com/tfranzel/drf-spectacular/releases)
- [Changelog](https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tfranzel/drf-spectacular/compare/0.26.3...0.26.4)

---
updated-dependencies:
- dependency-name: drf-spectacular
  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>
2023-07-24 11:50:52 +02:00
c50148072e core: bump ruff from 0.0.278 to 0.0.280 (#6344)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.278 to 0.0.280.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.278...v0.0.280)

---
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>
2023-07-24 11:50:40 +02:00
deda3a57ee website: bump postcss from 8.4.26 to 8.4.27 in /website (#6345)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.26 to 8.4.27.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.26...8.4.27)

---
updated-dependencies:
- dependency-name: postcss
  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>
2023-07-24 11:50:24 +02:00
8f0c0fae62 web: bump @esbuild/linux-arm64 from 0.18.15 to 0.18.16 in /web (#6347)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.15 to 0.18.16.
- [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.18.15...v0.18.16)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-24 11:50:12 +02:00
2015463fe0 web: bump @esbuild/darwin-arm64 from 0.18.15 to 0.18.16 in /web (#6346)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.15 to 0.18.16.
- [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.18.15...v0.18.16)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  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>
2023-07-24 10:30:10 +02:00
d435a65cfd outposts: support json patch for Kubernetes (#6319) 2023-07-22 02:29:28 +02:00
a728dad166 providers/oauth2: fix grant_type password raising an exception (#6333) 2023-07-22 01:36:55 +02:00
e0564b3770 web: bump API Client version (#6331) 2023-07-21 18:27:14 +02:00
d50f92d8b4 enterprise: cleanup v2 (#6330)
* cleanup minor stuff

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

* change default user type to internal to be more consistent

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 18:23:51 +02:00
03f3ad89df web: bump prettier from 2.8.8 to 3.0.0 in /web (#6329)
* web: bump prettier from 2.8.8 to 3.0.0 in /web

Bumps [prettier](https://github.com/prettier/prettier) from 2.8.8 to 3.0.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/2.8.8...3.0.0)

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

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

* update formatting and config

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>
2023-07-21 18:19:19 +02:00
e604e70395 web: bump @sentry/browser from 7.59.3 to 7.60.0 in /web (#6328)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.59.3 to 7.60.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/7.59.3...7.60.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-21 13:48:20 +02:00
1db048bdaf web: bump @trivago/prettier-plugin-sort-imports from 4.1.1 to 4.2.0 in /web (#6326)
web: bump @trivago/prettier-plugin-sort-imports in /web

Bumps [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports) from 4.1.1 to 4.2.0.
- [Release notes](https://github.com/trivago/prettier-plugin-sort-imports/releases)
- [Changelog](https://github.com/trivago/prettier-plugin-sort-imports/blob/main/CHANGELOG.md)
- [Commits](https://github.com/trivago/prettier-plugin-sort-imports/compare/v4.1.1...v4.2.0)

---
updated-dependencies:
- dependency-name: "@trivago/prettier-plugin-sort-imports"
  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>
2023-07-21 13:43:50 +02:00
3d973e7ce3 web: bump @sentry/tracing from 7.59.3 to 7.60.0 in /web (#6327)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.59.3 to 7.60.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/7.59.3...7.60.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-21 13:43:30 +02:00
9bc3327f03 web: bump @esbuild/darwin-arm64 from 0.18.14 to 0.18.15 in /web (#6322)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.14 to 0.18.15.
- [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.18.14...v0.18.15)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  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>
2023-07-21 13:39:45 +02:00
f1979e12cc web: bump @esbuild/linux-arm64 from 0.18.14 to 0.18.15 in /web (#6323)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.14 to 0.18.15.
- [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.18.14...v0.18.15)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-21 13:04:15 +02:00
121cc6ac98 web: bump @codemirror/legacy-modes from 6.3.2 to 6.3.3 in /web (#6324)
Bumps [@codemirror/legacy-modes](https://github.com/codemirror/legacy-modes) from 6.3.2 to 6.3.3.
- [Changelog](https://github.com/codemirror/legacy-modes/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/legacy-modes/compare/6.3.2...6.3.3)

---
updated-dependencies:
- dependency-name: "@codemirror/legacy-modes"
  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>
2023-07-21 13:04:04 +02:00
9b7c30d44c sources/ldap: fix ldap_sync cli command not running in foreground (#6325)
closes #6317

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 13:03:06 +02:00
82935ddf11 website/devdocs: updated hackathon page with exact times, other info (#6321)
updated hackathon page with exact times, other info

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-21 12:08:37 +02:00
989ff5a464 website/blog: fixed grammatical error in blog (#6318)
Update item.md

corrected grammar issue

Signed-off-by: Tana M Berry <tanamarieberry@yahoo.com>
2023-07-20 15:07:15 -05:00
0b5870f16e web: bump @sentry/browser from 7.59.2 to 7.59.3 in /web (#6312)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.59.2 to 7.59.3.
- [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.59.2...7.59.3)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-20 11:56:50 +02:00
36e16a270b web: bump eslint-plugin-storybook from 0.6.12 to 0.6.13 in /web (#6316)
Bumps [eslint-plugin-storybook](https://github.com/storybookjs/eslint-plugin-storybook) from 0.6.12 to 0.6.13.
- [Release notes](https://github.com/storybookjs/eslint-plugin-storybook/releases)
- [Changelog](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/eslint-plugin-storybook/compare/v0.6.12...v0.6.13)

---
updated-dependencies:
- dependency-name: eslint-plugin-storybook
  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>
2023-07-20 11:56:42 +02:00
09ffdea1f0 core: bump urllib3 from 2.0.3 to 2.0.4 (#6314)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.3 to 2.0.4.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.0.3...2.0.4)

---
updated-dependencies:
- dependency-name: urllib3
  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>
2023-07-20 11:51:11 +02:00
2889974e73 core: bump gunicorn from 21.1.0 to 21.2.0 (#6313)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 21.1.0 to 21.2.0.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/21.1.0...21.2.0)

---
updated-dependencies:
- dependency-name: gunicorn
  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>
2023-07-20 11:51:02 +02:00
15ce7423f6 web: bump @sentry/tracing from 7.59.2 to 7.59.3 in /web (#6311)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.59.2 to 7.59.3.
- [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.59.2...7.59.3)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-20 11:50:50 +02:00
d12db62a6d root: fix lint error
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-20 00:38:01 +02:00
546425acde root: fix config env var resolution (#6310)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-20 00:16:00 +02:00
7e46af3f45 website/blog: changed date to trick LinkedIn cache (#6309)
* chaged date to trick cache

* remove duplicate

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana Berry <tana@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-19 23:13:49 +02:00
2f469d2709 root: partial Live-updating config (#5959)
* stages/email: directly use email credentials from config

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

* use custom database backend that supports dynamic credentials

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

* fix tests

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

* add crude config reloader

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

* make method names for CONFIG clearer

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

* replace config.set with environ

Not sure if this is the cleanest way, but it persists through a config reload

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

* re-add set for @patch

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

* even more crudeness

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

* clean up some old stuff?

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

* somewhat rewrite config loader to keep track of a source of an attribute so we can refresh it

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

* cleanup old things

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

* fix flow e2e

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-19 23:13:22 +02:00
fb4e4dc8db website/blog: added image to header (#6307)
added image to header

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-19 21:36:13 +02:00
60d5936d73 website/blog: Ken blog, add graphic (#6306)
* added an image for the blog

* tweaks

---------

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-19 21:18:21 +02:00
28d9d4a16d website/blog: kens blog (#6305)
* kens blog

* removed white spaces in code blocks

* small formatting fix

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana Berry <tana@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-19 18:50:58 +00:00
31913a620d web/admin: include authentik_url in enterprise link (#6304)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-19 17:18:31 +02:00
2ac38869fe core: bump goauthentik.io/api/v3 from 3.2023061.2 to 3.2023061.3 (#6298)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.2 to 3.2023061.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.2...v3.2023061.3)

---
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>
2023-07-19 11:14:41 +02:00
9601d00a31 core: bump gunicorn from 21.0.1 to 21.1.0 (#6299)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 21.0.1 to 21.1.0.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/21.0.1...21.1.0)

---
updated-dependencies:
- dependency-name: gunicorn
  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>
2023-07-19 11:14:30 +02:00
e4358dafd7 core: bump pyjwt from 2.7.0 to 2.8.0 (#6300)
Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.7.0...2.8.0)

---
updated-dependencies:
- dependency-name: pyjwt
  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>
2023-07-19 11:14:21 +02:00
b144d28805 core: bump uvicorn from 0.23.0 to 0.23.1 (#6301)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.23.0 to 0.23.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.23.0...0.23.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>
2023-07-19 11:14:11 +02:00
e103eb9369 web: bump pyright from 1.1.317 to 1.1.318 in /web (#6302)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.317 to 1.1.318.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.318/packages/pyright)

---
updated-dependencies:
- dependency-name: pyright
  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>
2023-07-19 11:14:03 +02:00
e9dbab011f enterprise: more style fixes (#6297)
* fix horizontal scrollbar size

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

* fix horizontal scrollbar on user interface

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-19 01:31:45 +02:00
1ca3f15398 web: bump API Client version (#6295) 2023-07-18 23:27:36 +02:00
b6e8342466 enterprise: add more info to enterprise forecast (#6292)
* add more info to enterprise forecast

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

* fix banner colour

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

* fix some layout

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

* fix layout for warning banner

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-18 23:24:44 +02:00
c1eef9278d web: bump storybook from 7.0.27 to 7.1.0 in /web (#6287)
* web: bump storybook from 7.0.27 to 7.1.0 in /web

Bumps [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) from 7.0.27 to 7.1.0.
- [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/v7.1.0/code/lib/cli)

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

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

* bump all versions

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>
2023-07-18 19:41:55 +02:00
12c4ac704f web: basic cleanup of buttons (#6107)
* web: basic cleanup of buttons

This commit adds Storybook features to the Authentik four-stage button.
The four-stage button is used to:

- trigger an action
- show that the action is running
- show when the action has succeeded, then reset
- show when the action has failed, then reset

It is used mostly for fetching data from the server.  The variants are:

- ak-spinner-button: The basic form takes a single property argument, `callAction` a function that
  returns a Promise (an asynchronous function).
- ak-action-button: Takes an API request function (which are all asynchronous) and adapts it to the
  `callAction`. The only difference in behavior with the Spinner button is that on failure the error
  message will be displayed by a notification.
- ak-token-copy-button: A specialized button that, on success, pushes the content of the retrieved
  object into the clipboard.

Cleanup consisted of:

- removing a lot of the in-line code from the HTML, decluttering it and making more explicit what
  the behaviors of each button type are on success and on failure.
- Replacing the ad-hoc Promise management with Lit's own `Task` handler. The `Task` handler knows
  how to notify a Lit-Element of its own internal state change, making it ideal for objects like
  this button that need to change their appearance as a Promise'd task progresses from idle →
  running → (success or failure).
- Providing JSDoc strings for all of the properties, slots, attributes, elements, and events.
- Adding 'pointer-events: none' during the running phases of the action, to prevent the user from
  clicking the button multiple times and launching multiple queries.
- Emitting an event for every stage of the operation:
  - `ak-button-click` when the button is clicked.
  - `ak-button-success` when the action completes. The payload is included in `Event.detail.result`
  - `ak-button-failure` when the action fails. The error message is included in `Event.detail.error`
  - `ak-button-reset` when the button completes a notification and goes back to idle

**Storybook**

Since the API requests for both `ak-spinner-button` and `ak-action-button` require only that a
promise be returned, Storybooking them was straightforward. `ak-token-copy-button` is a
special-purpose derivative with an internal functionality that can't be easily mocked (yet), so
there's no Storybook for it.

All of the stories provide the required asynchronous function, in this cose one that waits three
seconds before emitting either a `response` or `reject` Promise.

`ak-action-button`'s Story has event handler code so that pressing on the button will result in a
message being written to a display block under the button.

I've added a new pair of class mixins, `CustomEmitterElement` and `CustomListenerElement`. These
each add an additional method to the classes they're mixed into; one provides a very easy way to
emit a custom event and one provides a way to receive the custom event while sweeping all of the
custom event type handling under the rug.

`emitCustomEvent` replaces this:

``` JavaScript
this.dispatchEvent(
  new CustomEvent('ak-button-click', {
    composed: true,
    bubbles: true,
    detail: {
      target: this,
      result: "Some result, huh?"
    },
  })
);
```

... with this:

``` JavaScript
this.dispatchCustomEvent('ak-button-click', { result: "Some result, huh?" });
```

The `CustomListenerElement` handler just ensures that the handler being passed to it takes a
CustomEvent, and then makes sure that any actual event passed to the handler has been type-guarded
to ensure it is a custom event.

**Observations**

*Composition vs Inheritance, Part 1*

The four-state button has three implementations.  All three inherit from `BaseTaskButton`:

- `spinner`
  - provides a default `callAction()`
- `action`
  - provides a different name for `callAction`
  - overrides `onError` to display a Notification.
- `token-copy`
  - provides a custom `callAction`
  - overrides `onSuccess` to copy the results to the keyboard
  - overrides `onError` to display a Notification, with special handling for asynchronous
    processing.

The *results* of all of these could be handled higher up as event handlers, and the button could be
just a thing that displays the states.  As it is, the BaseStateToken has only one reason to change
(the Promise changes its state), so I'm satisfied that this is a suitable evolution of the product,
and that it does what it says it does.

*Developer Ergonomics*

The one thing that stands out to me time and again is just how *confusing* all of the Patternfly
stuff tends to be; not because it's not logical, but because it overwhelms the human 7±2 ability to
remember details like this without any imperative to memorize all of them. I would like to get them
under control by marshalling them under a semantic CSS regime, but I'm blocked by some basic
disconnects in the current development environment.  We can't shake out the CSS as much as we'd like
because there's no ESPrima equivalent for Typescript, and the smallest bundle purgeCSS is capable of
making for just *one* button is about 55KB.  That's a bit too much.  It's a great system for getting
off the ground, but long-term it needs more love than we (can) give it.

* Prettier has opinions.

* Removed extraneous debugging code.

* Added comments to the BaseTaskButton parent class.

* web: fixed two build errors (typing) in the stories.

* web: prettier's got opinions

* web: refactor the buttons

This commit adds URL mocking to Storybook, which in turn allows us to
commit a Story for ak-token-copy-button.

I have confirmed that the button's algorithm for writing to the
clipboard works on Safari, Chrome, and Firefox.  I don't know
what's up with IE.

* ONE BYTE in .storybook/main blocked integration.

With the repair of lit-analyze, it's time to fix the rule set
to at least let us pass for the moment.

* Still looking for the list of exceptions in lit-analyze that will let us pass once more.

* web: repair error in EnterpriseLicenseForm

This commit continues to find the right configuration for
lit-analyze.  During the course of this repair, I discovered
a bug in the EnterpriseLicenseForm; the original usage could
result in the _string_ `undefined` being passed back as a
value.  To handle the case where the value truly is undefined,
the `ifDefined()` directive must be used in the HTML template.

I have also instituted a case-by-case stylistic decision to allow
the HTML, and only the HTML, to be longer that 100 characters
when doing so reduces the visual "noise" of a function.
2023-07-18 08:29:42 -07:00
14ebd55121 web: bump @storybook/addon-links from 7.0.27 to 7.1.0 in /web (#6282)
Bumps [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) from 7.0.27 to 7.1.0.
- [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/v7.1.0/code/addons/links)

---
updated-dependencies:
- dependency-name: "@storybook/addon-links"
  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>
2023-07-18 15:07:59 +02:00
5c7384eecc web: bump @sentry/browser from 7.58.1 to 7.59.2 in /web (#6286)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.58.1 to 7.59.2.
- [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.58.1...7.59.2)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-18 15:07:06 +02:00
cfbf7d3a9a web: bump @esbuild/linux-arm64 from 0.18.13 to 0.18.14 in /web (#6290)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.13 to 0.18.14.
- [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.18.13...v0.18.14)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-18 15:06:54 +02:00
f0cf4ba5d8 web: bump @sentry/tracing from 7.58.1 to 7.59.2 in /web (#6288)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.58.1 to 7.59.2.
- [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.58.1...7.59.2)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-18 15:05:09 +02:00
e207e8dff5 web: bump @typescript-eslint/eslint-plugin from 6.0.0 to 6.1.0 in /web (#6279)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.1.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  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>
2023-07-18 15:01:35 +02:00
c70d3bd182 web: Updates for file web/xliff/en.xlf in zh_CN on branch main (#6273)
* 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>
2023-07-18 14:15:31 +02:00
84a5e6a487 web: Updates for file web/xliff/en.xlf in zh-Hans on branch main (#6274)
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>
2023-07-18 14:15:19 +02:00
3a527b7680 core: bump goauthentik.io/api/v3 from 3.2023061.1 to 3.2023061.2 (#6275)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023061.1 to 3.2023061.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023061.1...v3.2023061.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-18 14:15:05 +02:00
a1c2931b3b core: bump gunicorn from 20.1.0 to 21.0.1 (#6276)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 20.1.0 to 21.0.1.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/20.1.0...21.0.1)

---
updated-dependencies:
- dependency-name: gunicorn
  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>
2023-07-18 14:14:55 +02:00
e67c0c2144 core: bump kubernetes from 26.1.0 to 27.2.0 (#6277)
Bumps [kubernetes](https://github.com/kubernetes-client/python) from 26.1.0 to 27.2.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/v26.1.0...v27.2.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>
2023-07-18 14:14:46 +02:00
5f8c06a088 core: bump pyyaml from 6.0 to 6.0.1 (#6278)
Bumps [pyyaml](https://github.com/yaml/pyyaml) from 6.0 to 6.0.1.
- [Changelog](https://github.com/yaml/pyyaml/blob/6.0.1/CHANGES)
- [Commits](https://github.com/yaml/pyyaml/compare/6.0...6.0.1)

---
updated-dependencies:
- dependency-name: pyyaml
  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>
2023-07-18 14:14:37 +02:00
b5fe8afd27 web: bump @typescript-eslint/parser from 6.0.0 to 6.1.0 in /web (#6280)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.1.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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>
2023-07-18 14:14:23 +02:00
d359dc5b09 enterprise: revise (#6272)
* web/admin: fix small enterprise things

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

* make website user branding consistent

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-17 23:07:24 +02:00
2e63a7c7e9 web: bump API Client version (#6271) 2023-07-17 17:59:52 +02:00
41af486006 enterprise: initial enterprise (#5721)
* initial

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

* add user type

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

* add external users

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

* add ui, add more logic, add public JWT validation key

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

* revert to not use install_id as session jwt signing key

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

* fix more

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

* switch to PKI

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

* add more licensing stuff

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

* add install ID to form

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

* fix bugs

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

* start adding tests

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

* fixes

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

* use x5c correctly

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

* license checks

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

* use production CA

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

* more

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

* more UI stuff

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

* rename to summary

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

* update locale, improve ui

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

* add direct button

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

* update link

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

* format and such

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

* remove old attributes from ldap

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

* remove is_enterprise_licensed

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

* fix

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

* fix admin interface styling issue

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

* Update authentik/core/models.py

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

* fix default case

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2023-07-17 17:57:08 +02:00
cf799fca03 sources/ldap: check nsaccountlock for FreeIPA/389-ds (#6270)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-17 12:59:29 +02:00
db4f61549d tests/e2e: improve assertCountEqual diff (#6261)
* tests/e2e: improve assertCountEqual diff

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

* fix

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-17 11:47:28 +02:00
27879d9d95 web: bump @esbuild/darwin-arm64 from 0.18.12 to 0.18.13 in /web (#6266)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.12 to 0.18.13.
- [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.18.12...v0.18.13)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  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>
2023-07-17 11:03:42 +02:00
1029b897ea website: bump clsx from 1.2.1 to 2.0.0 in /website (#6263)
Bumps [clsx](https://github.com/lukeed/clsx) from 1.2.1 to 2.0.0.
- [Release notes](https://github.com/lukeed/clsx/releases)
- [Commits](https://github.com/lukeed/clsx/compare/v1.2.1...v2.0.0)

---
updated-dependencies:
- dependency-name: clsx
  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>
2023-07-17 10:44:26 +02:00
85d1993ddf core: bump uvicorn from 0.22.0 to 0.23.0 (#6264)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.22.0 to 0.23.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.22.0...0.23.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>
2023-07-17 10:42:49 +02:00
de9ac08d91 web: bump eslint from 8.44.0 to 8.45.0 in /web (#6268)
Bumps [eslint](https://github.com/eslint/eslint) from 8.44.0 to 8.45.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.44.0...v8.45.0)

---
updated-dependencies:
- dependency-name: eslint
  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>
2023-07-17 10:42:29 +02:00
9a06908984 web: bump @rollup/plugin-commonjs from 25.0.2 to 25.0.3 in /web (#6269)
Bumps [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/HEAD/packages/commonjs) from 25.0.2 to 25.0.3.
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/commonjs/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v25.0.3/packages/commonjs)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-commonjs"
  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>
2023-07-17 10:42:18 +02:00
911d7f435b web: bump @esbuild/linux-arm64 from 0.18.12 to 0.18.13 in /web (#6267)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.12 to 0.18.13.
- [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.18.12...v0.18.13)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-17 10:42:04 +02:00
7eef86a3f4 website/blog: fixed bad link (#6257)
fixed bad link

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-14 19:43:52 +00:00
77662c9a51 website/docs: re-add goauthentik.io/user/can-change-* (#6251)
* website/docs: re-add goauthentik.io/user/can-change-*

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

* Update website/docs/user-group/user.md

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2023-07-14 16:28:37 +02:00
ca25c46ee1 core: bump twilio from 8.4.0 to 8.5.0 (#6254)
Bumps [twilio](https://github.com/twilio/twilio-python) from 8.4.0 to 8.5.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/8.4.0...8.5.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>
2023-07-14 12:15:59 +02:00
59ae774712 core: bump sentry-sdk from 1.28.0 to 1.28.1 (#6255)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.28.0 to 1.28.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/1.28.0...1.28.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>
2023-07-14 12:14:48 +02:00
c350560d59 website: bump postcss from 8.4.25 to 8.4.26 in /website (#6256)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.25 to 8.4.26.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.25...8.4.26)

---
updated-dependencies:
- dependency-name: postcss
  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>
2023-07-14 12:13:53 +02:00
810a4fd14a web: bump @sentry/browser from 7.58.0 to 7.58.1 in /web (#6248)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.58.0 to 7.58.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.58.0...7.58.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-13 17:01:52 +02:00
b4a1a1e664 web: bump @sentry/tracing from 7.57.0 to 7.58.1 in /web (#6249)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.57.0 to 7.58.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.57.0...7.58.1)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-13 16:33:47 +02:00
5ca65003f1 events: fix authentik_system_tasks metric status label (#6252)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-13 16:24:13 +02:00
b0bce60e5e web: bump @storybook/addon-links from 7.0.26 to 7.0.27 in /web (#6230)
Bumps [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) from 7.0.26 to 7.0.27.
- [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/v7.0.27/code/addons/links)

---
updated-dependencies:
- dependency-name: "@storybook/addon-links"
  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>
2023-07-13 13:16:26 +02:00
ff9b48a2d4 web: bump @sentry/tracing from 7.57.0 to 7.58.0 in /web (#6244)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.57.0 to 7.58.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/7.57.0...7.58.0)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  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>
2023-07-13 13:16:18 +02:00
8f1785924f web: bump pyright from 1.1.316 to 1.1.317 in /web (#6245)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.316 to 1.1.317.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.317/packages/pyright)

---
updated-dependencies:
- dependency-name: pyright
  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>
2023-07-13 13:16:09 +02:00
af25ba7508 web: bump @babel/core from 7.22.8 to 7.22.9 in /web (#6246)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.22.8 to 7.22.9.
- [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.22.9/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  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>
2023-07-13 13:15:49 +02:00
8ccd500d5b web: bump @babel/plugin-transform-runtime from 7.22.7 to 7.22.9 in /web (#6247)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.22.7 to 7.22.9.
- [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.22.9/packages/babel-plugin-transform-runtime)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-runtime"
  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>
2023-07-13 13:15:39 +02:00
40709e93de core: bump maxmind/geoipupdate from v5.1 to v6.0 (#6240)
* core: bump maxmind/geoipupdate from v5.1 to v6.0

Bumps maxmind/geoipupdate from v5.1 to v6.0.

---
updated-dependencies:
- dependency-name: maxmind/geoipupdate
  dependency-type: direct:production
...

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

* use new flags

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>
2023-07-13 12:51:37 +02:00
31cabbd64c web: bump @esbuild/linux-arm64 from 0.18.11 to 0.18.12 in /web (#6233)
Bumps [@esbuild/linux-arm64](https://github.com/evanw/esbuild) from 0.18.11 to 0.18.12.
- [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.18.11...v0.18.12)

---
updated-dependencies:
- dependency-name: "@esbuild/linux-arm64"
  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>
2023-07-13 12:51:21 +02:00
f7a0163a70 web: bump @babel/preset-env from 7.22.7 to 7.22.9 in /web (#6231)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.22.7 to 7.22.9.
- [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.22.9/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  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>
2023-07-13 12:49:11 +02:00
0db1d9598d web: bump @storybook/addon-essentials from 7.0.26 to 7.0.27 in /web (#6232)
Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) from 7.0.26 to 7.0.27.
- [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/v7.0.27/code/addons/essentials)

---
updated-dependencies:
- dependency-name: "@storybook/addon-essentials"
  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>
2023-07-13 12:48:43 +02:00
db8ae4e0f1 web: bump @esbuild/darwin-arm64 from 0.18.11 to 0.18.12 in /web (#6237)
Bumps [@esbuild/darwin-arm64](https://github.com/evanw/esbuild) from 0.18.11 to 0.18.12.
- [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.18.11...v0.18.12)

---
updated-dependencies:
- dependency-name: "@esbuild/darwin-arm64"
  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>
2023-07-13 12:48:20 +02:00
84542080d6 web: bump webcomponent-qr-code from 1.1.1 to 1.2.0 in /web (#6238)
Bumps [webcomponent-qr-code](https://github.com/educastellano/qr-code) from 1.1.1 to 1.2.0.
- [Commits](https://github.com/educastellano/qr-code/compare/v1.1.1...v1.2.0)

---
updated-dependencies:
- dependency-name: webcomponent-qr-code
  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>
2023-07-13 12:47:43 +02:00
a95ce95b50 web: bump @sentry/browser from 7.57.0 to 7.58.0 in /web (#6236)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.57.0 to 7.58.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/7.57.0...7.58.0)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  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>
2023-07-13 12:47:22 +02:00
e655683eec web: bump @storybook/web-components-vite from 7.0.26 to 7.0.27 in /web (#6239)
Bumps [@storybook/web-components-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/web-components-vite) from 7.0.26 to 7.0.27.
- [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/v7.0.27/code/frameworks/web-components-vite)

---
updated-dependencies:
- dependency-name: "@storybook/web-components-vite"
  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>
2023-07-13 12:47:12 +02:00
443b572413 web: bump storybook from 7.0.26 to 7.0.27 in /web (#6234)
Bumps [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) from 7.0.26 to 7.0.27.
- [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/v7.0.27/code/lib/cli)

---
updated-dependencies:
- dependency-name: storybook
  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>
2023-07-13 12:30:05 +02:00
6836ba2226 core: bump ruff from 0.0.277 to 0.0.278 (#6241)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.0.277 to 0.0.278.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.0.277...v0.0.278)

---
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>
2023-07-13 12:01:31 +02:00
1e3c9c26ea core: bump drf-jsonschema-serializer from 1.0.0 to 2.0.0 (#6242)
Bumps [drf-jsonschema-serializer](https://github.com/maykinmedia/drf-jsonschema-serializer) from 1.0.0 to 2.0.0.
- [Changelog](https://github.com/maykinmedia/drf-jsonschema-serializer/blob/main/docs/changelog.rst)
- [Commits](https://github.com/maykinmedia/drf-jsonschema-serializer/compare/1.0.0...2.0.0)

---
updated-dependencies:
- dependency-name: drf-jsonschema-serializer
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 12:01:19 +02:00
145f011eba website/blog: fixed wrong date (#6228)
* fixed wrong date

* ran prettier again

* fix formatting

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana Berry <tana@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-12 09:04:21 -05:00
095b5bfc78 root: Update security policy (#6222)
* Update security md

* update supported versions

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-12 06:56:37 -06:00
15d9f39a9e core: bump golang from 1.20.5-bullseye to 1.20.6-bullseye (#6226)
Bumps golang from 1.20.5-bullseye to 1.20.6-bullseye.

---
updated-dependencies:
- dependency-name: 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>
2023-07-12 11:48:11 +02:00
9d07f1e83e ci: bump helm/kind-action from 1.7.0 to 1.8.0 (#6227)
Bumps [helm/kind-action](https://github.com/helm/kind-action) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/helm/kind-action/releases)
- [Commits](https://github.com/helm/kind-action/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: helm/kind-action
  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>
2023-07-12 11:13:44 +02:00
f4e94bff1f web/admin: fix admin overview layout (#6220)
* web/admin: fix admin overview layout

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

* update locale

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-11 21:20:12 +02:00
6345c7fa8e website/blog: draft for 2nd hacakthon blog (#6219)
draft for 2nd hacakthon blog

Co-authored-by: Tana Berry <tana@goauthentik.io>
2023-07-11 13:17:02 -05:00
2e9dc2d5ea web: bump @typescript-eslint/eslint-plugin from 5.61.0 to 6.0.0 in /web (#6210)
* web: bump @typescript-eslint/eslint-plugin from 5.61.0 to 6.0.0 in /web

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.61.0 to 6.0.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.0.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* update both

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>
2023-07-11 14:32:50 +02:00
8f05f4d29c web: bump @typescript-eslint/parser from 5.61.0 to 5.62.0 in /web (#6209)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.61.0 to 5.62.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.62.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  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>
2023-07-11 11:17:32 +02:00
5b2496c190 website: bump semver from 5.7.1 to 5.7.2 in /website (#6215)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-11 11:17:18 +02:00
6893356c30 core: bump pytest-randomly from 3.12.0 to 3.13.0 (#6212)
Bumps [pytest-randomly](https://github.com/pytest-dev/pytest-randomly) from 3.12.0 to 3.13.0.
- [Changelog](https://github.com/pytest-dev/pytest-randomly/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-randomly/compare/3.12.0...3.13.0)

---
updated-dependencies:
- dependency-name: pytest-randomly
  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>
2023-07-11 11:09:08 +02:00
943608e554 core: bump black from 23.3.0 to 23.7.0 (#6211)
Bumps [black](https://github.com/psf/black) from 23.3.0 to 23.7.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/23.3.0...23.7.0)

---
updated-dependencies:
- dependency-name: black
  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>
2023-07-11 11:08:54 +02:00
6c065bd7e3 core: bump goauthentik.io/api/v3 from 3.2023060.1 to 3.2023061.1 (#6214)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023060.1 to 3.2023061.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023060.1...v3.2023061.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-11 11:08:35 +02:00
dfff445ddd core: bump sentry-sdk from 1.27.1 to 1.28.0 (#6213)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.27.1 to 1.28.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/1.27.1...1.28.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>
2023-07-11 11:08:18 +02:00
e08f8d5fb5 web: bump API Client version (#6208) 2023-07-10 15:09:23 +02:00
30a7a6cbe9 Merge branch 'version-2023.6' 2023-07-10 14:14:03 +02:00
57893e0125 website: update 2023.6.1 release notes (#6204)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-10 12:55:35 +02:00
d2c4bcf25d core: fix UUID filter field for users api (#6203)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-10 12:12:39 +02:00
c3560c3f05 core: bump ua-parser from 0.16.1 to 0.18.0 (#6197)
Bumps [ua-parser](https://github.com/ua-parser/uap-python) from 0.16.1 to 0.18.0.
- [Commits](https://github.com/ua-parser/uap-python/commits)

---
updated-dependencies:
- dependency-name: ua-parser
  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>
2023-07-10 11:33:42 +02:00
50bbb0a9d2 core: bump importlib-metadata from 6.7.0 to 6.8.0 (#6198)
Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 6.7.0 to 6.8.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/v6.7.0...v6.8.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>
2023-07-10 11:33:27 +02:00
6839c5b750 core: bump goauthentik.io/api/v3 from 3.2023054.4 to 3.2023060.1 (#6199)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2023054.4 to 3.2023060.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2023054.4...v3.2023060.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-10 11:33:16 +02:00
fcdf165dfe outposts/ldap: add test for attribute filtering (#6189)
add failing test case

Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-08 21:16:43 +02:00
ae7ea4dd11 outposts/ldap: add more tests (#6188)
* outposts/ldap: add tests

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

* fix missing posixAccount

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

* attempt to expand attributes

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

* fix routing without base DN

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

* more logging

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

* remove our custom attribute filtering since this is done by the ldap library

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

* add test for schema

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>
2023-07-08 20:51:05 +02:00
0c917ac3ed website/integrations: update wordpress integration docs (#6193)
Update integrations/services/wordpress/index.md

The question mark in the redirect uri as it's a regex special character and needs to be escaped for this to work.

Signed-off-by: Jens <silvercat@lavvu.net>
2023-07-08 14:44:09 +00:00
657c17a12f sources/ldap: fix more errors (#6191) 2023-07-08 02:32:47 +02:00
8828eefbe4 sources/ldap: fix page size (#6187)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-08 01:15:35 +02:00
02063f7d92 website/blog: MS monopoly (#6172)
* draft for blog on MS monopoly

* draft PR

* draft for blog

* fixed typo, ran linter
version: '3.4'

* fix linting with latest prettier

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Tana Berry <tana@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-07 20:39:30 +02:00
24244d6ff4 web: add required esbuild optional packages (#6181)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-07-07 16:36:17 +02:00
4e5ea05987 web: refactor locale handler into top-level context handler (#6022)
* web: begin refactoring the application for future development

This commit:

- Deletes a bit of code.
- Extracts *all* of the Locale logic into a single folder, turns management of the Locale files over
  to Lit itself, and restricts our responsibility to setting the locale on startup and when the user
  changes the locale. We do this by converting a lot of internal calls into events; a request to
  change a locale isn't a function call, it's an event emitted asking `REQUEST_LOCALE_CHANGE`. We've
  even eliminated the `DETECT_LOCALE_CHANGE` event, which redrew elements with text in them, since
  Lit's own `@localized()` decorator does that for us automagically.
- We wrap our interfaces in an `ak-locale-context` that handles the startup and listens for the
  `REQUEST_LOCALE_CHANGE` event.
- ... and that's pretty much it.  Adding `@localized()` as a default behavior to `AKElement` means
  no more custom localization is needed *anywhere*.

* web: improve the localization experience

This commit fixes the Storybook story for the localization context component,
and fixes the localization initialization pass so that it is only called once
per interface environment initialization.  Since all our interfaces share the
same environment (the Django server), this preserves functionality across
all interfaces.

---------

Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-07 14:23:10 +00:00
f8be8f2268 web: bump API Client version (#6179) 2023-07-07 15:35:41 +02:00
265 changed files with 14436 additions and 6988 deletions

View File

@ -112,7 +112,7 @@ jobs:
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.7.0
uses: helm/kind-action@v1.8.0
- name: run integration
run: |
poetry run coverage run manage.py test tests/integration

View File

@ -0,0 +1,39 @@
# Rename transifex pull requests to have a correct naming
name: authentik-translation-transifex-rename
on:
pull_request:
types: [opened, reopened]
jobs:
rename_pr:
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
steps:
- id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Get current title
id: title
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
title=$(curl -q -L \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} | jq -r .title)
echo "title=${title}" >> "$GITHUB_OUTPUT"
- name: Rename
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} \
-d "{\"title\":\"translate: ${{ steps.title.outputs.title }}\"}"

1
.gitignore vendored
View File

@ -204,3 +204,4 @@ data/
# Local Netlify folder
.netlify
.ruff_cache

View File

@ -31,7 +31,7 @@ RUN pip install --no-cache-dir poetry && \
poetry export -f requirements.txt --dev --output requirements-dev.txt
# Stage 4: Build go proxy
FROM docker.io/golang:1.20.5-bullseye AS go-builder
FROM docker.io/golang:1.20.6-bullseye AS go-builder
WORKDIR /work
@ -47,20 +47,18 @@ COPY ./go.sum /work/go.sum
RUN go build -o /work/authentik ./cmd/server/
# Stage 5: MaxMind GeoIP
FROM ghcr.io/maxmind/geoipupdate:v5.1 as geoip
FROM ghcr.io/maxmind/geoipupdate:v6.0 as geoip
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City"
ENV GEOIPUPDATE_VERBOSE="true"
ENV GEOIPUPDATE_ACCOUNT_ID_FILE="/run/secrets/GEOIPUPDATE_ACCOUNT_ID"
ENV GEOIPUPDATE_LICENSE_KEY_FILE="/run/secrets/GEOIPUPDATE_LICENSE_KEY"
USER root
RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
--mount=type=secret,id=GEOIPUPDATE_LICENSE_KEY \
mkdir -p /usr/share/GeoIP && \
/bin/sh -c "\
export GEOIPUPDATE_ACCOUNT_ID=$(cat /run/secrets/GEOIPUPDATE_ACCOUNT_ID); \
export GEOIPUPDATE_LICENSE_KEY=$(cat /run/secrets/GEOIPUPDATE_LICENSE_KEY); \
/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0 \
"
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
# Stage 6: Run
FROM docker.io/python:3.11.4-slim-bullseye AS final-image

View File

@ -145,7 +145,8 @@ web-lint-fix:
web-lint:
cd web && npm run lint
cd web && npm run lit-analyse
# TODO: The analyzer hasn't run correctly in awhile.
# cd web && npm run lit-analyse
web-check-compile:
cd web && npm run tsc

View File

@ -1,44 +1,48 @@
authentik takes security very seriously. We follow the rules of [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure), and we urge our community to do so as well, instead of reporting vulnerabilities publicly. This allows us to patch the issue quickly, announce it's existence and release the fixed version.
authentik takes security very seriously. We follow the rules of [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure), and we urge our community to do so as well, instead of reporting vulnerabilities publicly. This allows us to patch the issue quickly, announce it's existence and release the fixed version.
## What authentik classifies as a CVE
CVE (Common Vulnerability and Exposure) is a system designed to aggregate all vulnerabilities. As such, a CVE will be issued when there is a either vulnerability or exposure. Per NIST, A vulnerability is:
“Weakness in an information system, system security procedures, internal controls, or implementation that could be exploited or triggered by a threat source.”
If it is determined that the issue does qualify as a CVE, a CVE number will be issued to the reporter from GitHub.
Even if the issue is not a CVE, we still greatly appreciate your help in hardening authentik.
## Supported Versions
(.x being the latest patch release for each version)
| Version | Supported |
| --------- | ------------------ |
| 2023.4.x | :white_check_mark: |
| 2023.5.x | :white_check_mark: |
| Version | Supported |
| --- | --- |
| 2023.5.x | |
| 2023.6.x | |
## Reporting a Vulnerability
To report a vulnerability, send an email to [security@goauthentik.io](mailto:security@goauthentik.io). Be sure to include relevant information like which version you've found the issue in, instructions on how to reproduce the issue, and anything else that might make it easier for us to find the bug.
To report a vulnerability, send an email to [security@goauthentik.io](mailto:security@goauthentik.io). Be sure to include relevant information like which version you've found the issue in, instructions on how to reproduce the issue, and anything else that might make it easier for us to find the issue.
## Criticality levels
## Severity levels
### High
authentik reserves the right to reclassify CVSS as necessary. To determine severity, we will use the CVSS calculator from NVD (https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). The calculated CVSS score will then be translated into one of the following categories:
- Authorization bypass
- Circumvention of policies
### Moderate
- Denial-of-Service attacks
### Low
- Unvalidated redirects
- Issues requiring uncommon setups
| 0.0 | None |
| 0.1 3.9 | Low |
| 4.0 6.9 | Medium |
| 7.0 8.9 | High |
| 9.0 10.0 | Critical |
## Disclosure process
1. Issue is reported via Email as listed above.
1. Report from Github or Issue is reported via Email as listed above.
2. The authentik Security team will try to reproduce the issue and ask for more information if required.
3. A criticality level is assigned.
3. A severity level is assigned.
4. A fix is created, and if possible tested by the issue reporter.
5. The fix is backported to other supported versions, and if possible a workaround for other versions is created.
6. An announcement is sent out with a fixed release date and criticality level of the issue. The announcement will be sent at least 24 hours before the release of the fix
6. An announcement is sent out with a fixed release date and severity level of the issue. The announcement will be sent at least 24 hours before the release of the security fix.
7. The fixed version is released for the supported versions.
## Getting security notifications
To get security notifications, subscribe to the mailing list [here](https://groups.google.com/g/authentik-security-announcements) or join the [discord](https://goauthentik.io/discord) server.
To get security notifications, subscribe to the mailing list [here](https://groups.google.com/g/authentik-security-announcements) or join the [discord](https://goauthentik.io/discord) server.

View File

@ -58,7 +58,7 @@ def clear_update_notifications():
@prefill_task
def update_latest_version(self: MonitoredTask):
"""Update latest version info"""
if CONFIG.y_bool("disable_update_check"):
if CONFIG.get_bool("disable_update_check"):
cache.set(VERSION_CACHE_KEY, "0.0.0", VERSION_CACHE_TIMEOUT)
self.set_status(TaskResult(TaskResultStatus.WARNING, messages=["Version check disabled."]))
return

View File

@ -9,7 +9,7 @@ from rest_framework.exceptions import AuthenticationFailed
from authentik.api.authentication import bearer_auth
from authentik.blueprints.tests import reconcile_app
from authentik.core.models import USER_ATTRIBUTE_SA, Token, TokenIntents
from authentik.core.models import Token, TokenIntents, User, UserTypes
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.lib.generators import generate_id
from authentik.providers.oauth2.constants import SCOPE_AUTHENTIK_API
@ -57,8 +57,8 @@ class TestAPIAuth(TestCase):
@reconcile_app("authentik_outposts")
def test_managed_outpost_success(self):
"""Test managed outpost"""
user = bearer_auth(f"Bearer {settings.SECRET_KEY}".encode())
self.assertEqual(user.attributes[USER_ATTRIBUTE_SA], True)
user: User = bearer_auth(f"Bearer {settings.SECRET_KEY}".encode())
self.assertEqual(user.type, UserTypes.INTERNAL_SERVICE_ACCOUNT)
def test_jwt_valid(self):
"""Test valid JWT"""

View File

@ -3,6 +3,7 @@ from pathlib import Path
from django.conf import settings
from django.db import models
from django.dispatch import Signal
from drf_spectacular.utils import extend_schema
from rest_framework.fields import (
BooleanField,
@ -21,6 +22,8 @@ from authentik.core.api.utils import PassiveSerializer
from authentik.events.geo import GEOIP_READER
from authentik.lib.config import CONFIG
capabilities = Signal()
class Capabilities(models.TextChoices):
"""Define capabilities which influence which APIs can/should be used"""
@ -67,12 +70,15 @@ class ConfigView(APIView):
caps.append(Capabilities.CAN_SAVE_MEDIA)
if GEOIP_READER.enabled:
caps.append(Capabilities.CAN_GEO_IP)
if CONFIG.y_bool("impersonation"):
if CONFIG.get_bool("impersonation"):
caps.append(Capabilities.CAN_IMPERSONATE)
if settings.DEBUG: # pragma: no cover
caps.append(Capabilities.CAN_DEBUG)
if "authentik.enterprise" in settings.INSTALLED_APPS:
caps.append(Capabilities.IS_ENTERPRISE)
for _, result in capabilities.send(sender=self):
if result:
caps.append(result)
return caps
def get_config(self) -> ConfigSerializer:
@ -80,17 +86,17 @@ class ConfigView(APIView):
return ConfigSerializer(
{
"error_reporting": {
"enabled": CONFIG.y("error_reporting.enabled"),
"sentry_dsn": CONFIG.y("error_reporting.sentry_dsn"),
"environment": CONFIG.y("error_reporting.environment"),
"send_pii": CONFIG.y("error_reporting.send_pii"),
"traces_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.4)),
"enabled": CONFIG.get("error_reporting.enabled"),
"sentry_dsn": CONFIG.get("error_reporting.sentry_dsn"),
"environment": CONFIG.get("error_reporting.environment"),
"send_pii": CONFIG.get("error_reporting.send_pii"),
"traces_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.4)),
},
"capabilities": self.get_capabilities(),
"cache_timeout": int(CONFIG.y("redis.cache_timeout")),
"cache_timeout_flows": int(CONFIG.y("redis.cache_timeout_flows")),
"cache_timeout_policies": int(CONFIG.y("redis.cache_timeout_policies")),
"cache_timeout_reputation": int(CONFIG.y("redis.cache_timeout_reputation")),
"cache_timeout": int(CONFIG.get("redis.cache_timeout")),
"cache_timeout_flows": int(CONFIG.get("redis.cache_timeout_flows")),
"cache_timeout_policies": int(CONFIG.get("redis.cache_timeout_policies")),
"cache_timeout_reputation": int(CONFIG.get("redis.cache_timeout_reputation")),
}
)

View File

@ -21,9 +21,14 @@ _other_urls = []
for _authentik_app in get_apps():
try:
api_urls = import_module(f"{_authentik_app.name}.urls")
except (ModuleNotFoundError, ImportError):
except (ModuleNotFoundError, ImportError) as exc:
LOGGER.warning("Could not import app's URLs", app_name=_authentik_app.name, exc=exc)
continue
if not hasattr(api_urls, "api_urlpatterns"):
LOGGER.debug(
"App does not define API URLs",
app_name=_authentik_app.name,
)
continue
urls: list = getattr(api_urls, "api_urlpatterns")
for url in urls:

View File

@ -30,7 +30,7 @@ def check_blueprint_v1_file(BlueprintInstance: type, path: Path):
return
blueprint_file.seek(0)
instance: BlueprintInstance = BlueprintInstance.objects.filter(path=path).first()
rel_path = path.relative_to(Path(CONFIG.y("blueprints_dir")))
rel_path = path.relative_to(Path(CONFIG.get("blueprints_dir")))
meta = None
if metadata:
meta = from_dict(BlueprintMetadata, metadata)
@ -55,7 +55,7 @@ def migration_blueprint_import(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
Flow = apps.get_model("authentik_flows", "Flow")
db_alias = schema_editor.connection.alias
for file in glob(f"{CONFIG.y('blueprints_dir')}/**/*.yaml", recursive=True):
for file in glob(f"{CONFIG.get('blueprints_dir')}/**/*.yaml", recursive=True):
check_blueprint_v1_file(BlueprintInstance, Path(file))
for blueprint in BlueprintInstance.objects.using(db_alias).all():

View File

@ -82,7 +82,7 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel):
def retrieve_file(self) -> str:
"""Get blueprint from path"""
try:
base = Path(CONFIG.y("blueprints_dir"))
base = Path(CONFIG.get("blueprints_dir"))
full_path = base.joinpath(Path(self.path)).resolve()
if not str(full_path).startswith(str(base.resolve())):
raise BlueprintRetrievalFailed("Invalid blueprint path")

View File

@ -62,7 +62,7 @@ def start_blueprint_watcher():
if _file_watcher_started:
return
observer = Observer()
observer.schedule(BlueprintEventHandler(), CONFIG.y("blueprints_dir"), recursive=True)
observer.schedule(BlueprintEventHandler(), CONFIG.get("blueprints_dir"), recursive=True)
observer.start()
_file_watcher_started = True
@ -80,7 +80,7 @@ class BlueprintEventHandler(FileSystemEventHandler):
blueprints_discovery.delay()
if isinstance(event, FileModifiedEvent):
path = Path(event.src_path)
root = Path(CONFIG.y("blueprints_dir")).absolute()
root = Path(CONFIG.get("blueprints_dir")).absolute()
rel_path = str(path.relative_to(root))
for instance in BlueprintInstance.objects.filter(path=rel_path):
LOGGER.debug("modified blueprint file, starting apply", instance=instance)
@ -101,7 +101,7 @@ def blueprints_find_dict():
def blueprints_find():
"""Find blueprints and return valid ones"""
blueprints = []
root = Path(CONFIG.y("blueprints_dir"))
root = Path(CONFIG.get("blueprints_dir"))
for path in root.rglob("**/*.yaml"):
# Check if any part in the path starts with a dot and assume a hidden file
if any(part for part in path.parts if part.startswith(".")):

View File

@ -59,7 +59,6 @@ from authentik.core.middleware import (
SESSION_KEY_IMPERSONATE_USER,
)
from authentik.core.models import (
USER_ATTRIBUTE_SA,
USER_ATTRIBUTE_TOKEN_EXPIRING,
USER_PATH_SERVICE_ACCOUNT,
AuthenticatedSession,
@ -67,6 +66,7 @@ from authentik.core.models import (
Token,
TokenIntents,
User,
UserTypes,
)
from authentik.events.models import Event, EventAction
from authentik.flows.exceptions import FlowNonApplicableException
@ -147,6 +147,18 @@ class UserSerializer(ModelSerializer):
raise ValidationError(_("No empty segments in user path allowed."))
return path
def validate_type(self, user_type: str) -> str:
"""Validate user type, internal_service_account is an internal value"""
if (
self.instance
and self.instance.type == UserTypes.INTERNAL_SERVICE_ACCOUNT
and user_type != UserTypes.INTERNAL_SERVICE_ACCOUNT.value
):
raise ValidationError("Can't change internal service account to other user type.")
if not self.instance and user_type == UserTypes.INTERNAL_SERVICE_ACCOUNT.value:
raise ValidationError("Setting a user to internal service account is not allowed.")
return user_type
class Meta:
model = User
fields = [
@ -163,6 +175,7 @@ class UserSerializer(ModelSerializer):
"attributes",
"uid",
"path",
"type",
]
extra_kwargs = {
"name": {"allow_blank": True},
@ -211,6 +224,7 @@ class UserSelfSerializer(ModelSerializer):
"avatar",
"uid",
"settings",
"type",
]
extra_kwargs = {
"is_active": {"read_only": True},
@ -329,6 +343,7 @@ class UsersFilter(FilterSet):
"attributes",
"groups_by_name",
"groups_by_pk",
"type",
]
@ -421,7 +436,8 @@ class UserViewSet(UsedByMixin, ModelViewSet):
user: User = User.objects.create(
username=username,
name=username,
attributes={USER_ATTRIBUTE_SA: True, USER_ATTRIBUTE_TOKEN_EXPIRING: expiring},
type=UserTypes.SERVICE_ACCOUNT,
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: expiring},
path=USER_PATH_SERVICE_ACCOUNT,
)
user.set_unusable_password()
@ -580,7 +596,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
@action(detail=True, methods=["POST"])
def impersonate(self, request: Request, pk: int) -> Response:
"""Impersonate a user"""
if not CONFIG.y_bool("impersonation"):
if not CONFIG.get_bool("impersonation"):
LOGGER.debug("User attempted to impersonate", user=request.user)
return Response(status=401)
if not request.user.has_perm("impersonate"):

View File

@ -18,7 +18,7 @@ class Command(BaseCommand):
def handle(self, **options):
close_old_connections()
if CONFIG.y_bool("remote_debug"):
if CONFIG.get_bool("remote_debug"):
import debugpy
debugpy.listen(("0.0.0.0", 6900)) # nosec

View File

@ -0,0 +1,43 @@
# Generated by Django 4.1.7 on 2023-05-21 11:44
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_user_type(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
User = apps.get_model("authentik_core", "User")
from authentik.core.models import UserTypes
for user in User.objects.using(db_alias).all():
user.type = UserTypes.INTERNAL
if "goauthentik.io/user/service-account" in user.attributes:
user.type = UserTypes.SERVICE_ACCOUNT
if "goauthentik.io/user/override-ips" in user.attributes:
user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
user.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0029_provider_backchannel_applications_and_more"),
]
operations = [
migrations.AddField(
model_name="user",
name="type",
field=models.TextField(
choices=[
("default", "Default"),
("external", "External"),
("service_account", "Service Account"),
("internal_service_account", "Internal Service Account"),
],
default="default",
),
),
migrations.RunPython(migrate_user_type),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 4.1.10 on 2023-07-21 12:54
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_user_type_v2(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
User = apps.get_model("authentik_core", "User")
from authentik.core.models import UserTypes
for user in User.objects.using(db_alias).all():
if user.type != "default":
continue
user.type = UserTypes.INTERNAL
user.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0030_user_type"),
]
operations = [
migrations.AlterField(
model_name="user",
name="type",
field=models.TextField(
choices=[
("internal", "Internal"),
("external", "External"),
("service_account", "Service Account"),
("internal_service_account", "Internal Service Account"),
],
default="internal",
),
),
migrations.RunPython(migrate_user_type_v2),
]

View File

@ -36,7 +36,6 @@ from authentik.root.install_id import get_install_id
LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
USER_ATTRIBUTE_GENERATED = "goauthentik.io/user/generated"
USER_ATTRIBUTE_EXPIRES = "goauthentik.io/user/expires"
USER_ATTRIBUTE_DELETE_ON_LOGOUT = "goauthentik.io/user/delete-on-logout"
@ -45,8 +44,6 @@ USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec
USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username"
USER_ATTRIBUTE_CHANGE_NAME = "goauthentik.io/user/can-change-name"
USER_ATTRIBUTE_CHANGE_EMAIL = "goauthentik.io/user/can-change-email"
USER_ATTRIBUTE_CAN_OVERRIDE_IP = "goauthentik.io/user/override-ips"
USER_PATH_SYSTEM_PREFIX = "goauthentik.io"
USER_PATH_SERVICE_ACCOUNT = USER_PATH_SYSTEM_PREFIX + "/service-accounts"
@ -63,7 +60,22 @@ def default_token_key():
"""Default token key"""
# We use generate_id since the chars in the key should be easy
# to use in Emails (for verification) and URLs (for recovery)
return generate_id(int(CONFIG.y("default_token_length")))
return generate_id(int(CONFIG.get("default_token_length")))
class UserTypes(models.TextChoices):
"""User types, both for grouping, licensing and permissions in the case
of the internal_service_account"""
INTERNAL = "internal"
EXTERNAL = "external"
# User-created service accounts
SERVICE_ACCOUNT = "service_account"
# Special user type for internally managed and created service
# accounts, such as outpost users
INTERNAL_SERVICE_ACCOUNT = "internal_service_account"
class Group(SerializerModel):
@ -149,6 +161,7 @@ class User(SerializerModel, GuardianUserMixin, AbstractUser):
uuid = models.UUIDField(default=uuid4, editable=False, unique=True)
name = models.TextField(help_text=_("User's display name."))
path = models.TextField(default="users")
type = models.TextField(choices=UserTypes.choices, default=UserTypes.INTERNAL)
sources = models.ManyToManyField("Source", through="UserSourceConnection")
ak_groups = models.ManyToManyField("Group", related_name="users")

View File

@ -1,6 +1,4 @@
"""authentik core signals"""
from typing import TYPE_CHECKING
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
@ -10,16 +8,13 @@ from django.db.models.signals import post_save, pre_delete, pre_save
from django.dispatch import receiver
from django.http.request import HttpRequest
from authentik.core.models import Application, AuthenticatedSession, BackchannelProvider
from authentik.core.models import Application, AuthenticatedSession, BackchannelProvider, User
# Arguments: user: User, password: str
password_changed = Signal()
# Arguments: credentials: dict[str, any], request: HttpRequest, stage: Stage
login_failed = Signal()
if TYPE_CHECKING:
from authentik.core.models import User
@receiver(post_save, sender=Application)
def post_save_application(sender: type[Model], instance, created: bool, **_):
@ -35,7 +30,7 @@ def post_save_application(sender: type[Model], instance, created: bool, **_):
@receiver(user_logged_in)
def user_logged_in_session(sender, request: HttpRequest, user: "User", **_):
def user_logged_in_session(sender, request: HttpRequest, user: User, **_):
"""Create an AuthenticatedSession from request"""
session = AuthenticatedSession.from_request(request, user)
@ -44,7 +39,7 @@ def user_logged_in_session(sender, request: HttpRequest, user: "User", **_):
@receiver(user_logged_out)
def user_logged_out_session(sender, request: HttpRequest, user: "User", **_):
def user_logged_out_session(sender, request: HttpRequest, user: User, **_):
"""Delete AuthenticatedSession if it exists"""
AuthenticatedSession.objects.filter(session_key=request.session.session_key).delete()

View File

@ -8,11 +8,11 @@ from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.models import (
USER_ATTRIBUTE_SA,
USER_ATTRIBUTE_TOKEN_EXPIRING,
AuthenticatedSession,
Token,
User,
UserTypes,
)
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant
from authentik.flows.models import FlowDesignation
@ -141,7 +141,8 @@ class TestUsersAPI(APITestCase):
user_filter = User.objects.filter(
username="test-sa",
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True, USER_ATTRIBUTE_SA: True},
type=UserTypes.SERVICE_ACCOUNT,
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True},
)
self.assertTrue(user_filter.exists())
user: User = user_filter.first()
@ -166,7 +167,8 @@ class TestUsersAPI(APITestCase):
user_filter = User.objects.filter(
username="test-sa",
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: False, USER_ATTRIBUTE_SA: True},
type=UserTypes.SERVICE_ACCOUNT,
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: False},
)
self.assertTrue(user_filter.exists())
user: User = user_filter.first()
@ -192,7 +194,8 @@ class TestUsersAPI(APITestCase):
user_filter = User.objects.filter(
username="test-sa",
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True, USER_ATTRIBUTE_SA: True},
type=UserTypes.SERVICE_ACCOUNT,
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True},
)
self.assertTrue(user_filter.exists())
user: User = user_filter.first()
@ -218,7 +221,8 @@ class TestUsersAPI(APITestCase):
user_filter = User.objects.filter(
username="test-sa",
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True, USER_ATTRIBUTE_SA: True},
type=UserTypes.SERVICE_ACCOUNT,
attributes={USER_ATTRIBUTE_TOKEN_EXPIRING: True},
)
self.assertTrue(user_filter.exists())
user: User = user_filter.first()

View File

@ -46,7 +46,7 @@ def certificate_discovery(self: MonitoredTask):
certs = {}
private_keys = {}
discovered = 0
for file in glob(CONFIG.y("cert_discovery_dir") + "/**", recursive=True):
for file in glob(CONFIG.get("cert_discovery_dir") + "/**", recursive=True):
path = Path(file)
if not path.exists():
continue

View File

154
authentik/enterprise/api.py Normal file
View File

@ -0,0 +1,154 @@
"""Enterprise API Views"""
from datetime import datetime, timedelta
from django.utils.timezone import now
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField, DateTimeField, IntegerField
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from authentik.api.decorators import permission_required
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import User, UserTypes
from authentik.enterprise.models import License, LicenseKey
from authentik.root.install_id import get_install_id
class LicenseSerializer(ModelSerializer):
"""License Serializer"""
def validate_key(self, key: str) -> str:
"""Validate the license key (install_id and signature)"""
LicenseKey.validate(key)
return key
class Meta:
model = License
fields = [
"license_uuid",
"name",
"key",
"expiry",
"users",
"external_users",
]
extra_kwargs = {
"name": {"read_only": True},
"expiry": {"read_only": True},
"users": {"read_only": True},
"external_users": {"read_only": True},
}
class LicenseSummary(PassiveSerializer):
"""Serializer for license status"""
users = IntegerField(required=True)
external_users = IntegerField(required=True)
valid = BooleanField()
show_admin_warning = BooleanField()
show_user_warning = BooleanField()
read_only = BooleanField()
latest_valid = DateTimeField()
has_license = BooleanField()
class LicenseForecastSerializer(PassiveSerializer):
"""Serializer for license forecast"""
users = IntegerField(required=True)
external_users = IntegerField(required=True)
forecasted_users = IntegerField(required=True)
forecasted_external_users = IntegerField(required=True)
class LicenseViewSet(UsedByMixin, ModelViewSet):
"""License Viewset"""
queryset = License.objects.all()
serializer_class = LicenseSerializer
search_fields = ["name"]
ordering = ["name"]
filterset_fields = ["name"]
@permission_required(None, ["authentik_enterprise.view_license"])
@extend_schema(
request=OpenApiTypes.NONE,
responses={
200: inline_serializer("InstallIDSerializer", {"install_id": CharField(required=True)}),
},
)
@action(detail=False, methods=["GET"], permission_classes=[IsAdminUser])
def get_install_id(self, request: Request) -> Response:
"""Get install_id"""
return Response(
data={
"install_id": get_install_id(),
}
)
@extend_schema(
request=OpenApiTypes.NONE,
responses={
200: LicenseSummary(),
},
)
@action(detail=False, methods=["GET"], permission_classes=[IsAuthenticated])
def summary(self, request: Request) -> Response:
"""Get the total license status"""
total = LicenseKey.get_total()
last_valid = LicenseKey.last_valid_date()
# TODO: move this to a different place?
show_admin_warning = last_valid < now() - timedelta(weeks=2)
show_user_warning = last_valid < now() - timedelta(weeks=4)
read_only = last_valid < now() - timedelta(weeks=6)
latest_valid = datetime.fromtimestamp(total.exp)
response = LicenseSummary(
data={
"users": total.users,
"external_users": total.external_users,
"valid": total.is_valid(),
"show_admin_warning": show_admin_warning,
"show_user_warning": show_user_warning,
"read_only": read_only,
"latest_valid": latest_valid,
"has_license": License.objects.all().count() > 0,
}
)
response.is_valid(raise_exception=True)
return Response(response.data)
@permission_required(None, ["authentik_enterprise.view_license"])
@extend_schema(
request=OpenApiTypes.NONE,
responses={
200: LicenseForecastSerializer(),
},
)
@action(detail=False, methods=["GET"])
def forecast(self, request: Request) -> Response:
"""Forecast how many users will be required in a year"""
last_month = now() - timedelta(days=30)
# Forecast for default users
users_in_last_month = User.objects.filter(
type=UserTypes.INTERNAL, date_joined__gte=last_month
).count()
# Forecast for external users
external_in_last_month = LicenseKey.get_external_user_count()
forecast_for_months = 12
response = LicenseForecastSerializer(
data={
"users": LicenseKey.get_default_user_count(),
"external_users": LicenseKey.get_external_user_count(),
"forecasted_users": (users_in_last_month * forecast_for_months),
"forecasted_external_users": (external_in_last_month * forecast_for_months),
}
)
response.is_valid(raise_exception=True)
return Response(response.data)

View File

@ -9,3 +9,7 @@ class AuthentikEnterpriseConfig(ManagedAppConfig):
label = "authentik_enterprise"
verbose_name = "authentik Enterprise"
default = True
def reconcile_load_enterprise_signals(self):
"""Load enterprise signals"""
self.import_module("authentik.enterprise.signals")

View File

@ -0,0 +1,52 @@
# Generated by Django 4.1.10 on 2023-07-06 12:51
import uuid
from django.db import migrations, models
import authentik.enterprise.models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="License",
fields=[
(
"license_uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("key", models.TextField(unique=True)),
("name", models.TextField()),
("expiry", models.DateTimeField()),
("users", models.BigIntegerField()),
("external_users", models.BigIntegerField()),
],
),
migrations.CreateModel(
name="LicenseUsage",
fields=[
("expiring", models.BooleanField(default=True)),
("expires", models.DateTimeField(default=authentik.enterprise.models.usage_expiry)),
(
"usage_uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
("user_count", models.BigIntegerField()),
("external_user_count", models.BigIntegerField()),
("within_limits", models.BooleanField()),
("record_date", models.DateTimeField(auto_now_add=True)),
],
options={
"abstract": False,
},
),
]

View File

@ -0,0 +1,185 @@
"""Enterprise models"""
from base64 import b64decode
from binascii import Error
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from functools import lru_cache
from time import mktime
from uuid import uuid4
from cryptography.exceptions import InvalidSignature
from cryptography.x509 import Certificate, load_der_x509_certificate, load_pem_x509_certificate
from dacite import from_dict
from django.db import models
from django.db.models.query import QuerySet
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from jwt import PyJWTError, decode, get_unverified_header
from rest_framework.exceptions import ValidationError
from authentik.core.models import ExpiringModel, User, UserTypes
from authentik.root.install_id import get_install_id
@lru_cache()
def get_licensing_key() -> Certificate:
"""Get Root CA PEM"""
with open("authentik/enterprise/public.pem", "rb") as _key:
return load_pem_x509_certificate(_key.read())
def get_license_aud() -> str:
"""Get the JWT audience field"""
return f"enterprise.goauthentik.io/license/{get_install_id()}"
class LicenseFlags(Enum):
"""License flags"""
@dataclass
class LicenseKey:
"""License JWT claims"""
aud: str
exp: int
name: str
users: int
external_users: int
flags: list[LicenseFlags] = field(default_factory=list)
@staticmethod
def validate(jwt: str) -> "LicenseKey":
"""Validate the license from a given JWT"""
try:
headers = get_unverified_header(jwt)
except PyJWTError:
raise ValidationError("Unable to verify license")
x5c: list[str] = headers.get("x5c", [])
if len(x5c) < 1:
raise ValidationError("Unable to verify license")
try:
our_cert = load_der_x509_certificate(b64decode(x5c[0]))
intermediate = load_der_x509_certificate(b64decode(x5c[1]))
our_cert.verify_directly_issued_by(intermediate)
intermediate.verify_directly_issued_by(get_licensing_key())
except (InvalidSignature, TypeError, ValueError, Error):
raise ValidationError("Unable to verify license")
try:
body = from_dict(
LicenseKey,
decode(
jwt,
our_cert.public_key(),
algorithms=["ES512"],
audience=get_license_aud(),
),
)
except PyJWTError:
raise ValidationError("Unable to verify license")
return body
@staticmethod
def get_total() -> "LicenseKey":
"""Get a summarized version of all (not expired) licenses"""
active_licenses = License.objects.filter(expiry__gte=now())
total = LicenseKey(get_license_aud(), 0, "Summarized license", 0, 0)
for lic in active_licenses:
total.users += lic.users
total.external_users += lic.external_users
exp_ts = int(mktime(lic.expiry.timetuple()))
if total.exp == 0:
total.exp = exp_ts
if exp_ts <= total.exp:
total.exp = exp_ts
total.flags.extend(lic.status.flags)
return total
@staticmethod
def base_user_qs() -> QuerySet:
"""Base query set for all users"""
return User.objects.all().exclude(pk=get_anonymous_user().pk)
@staticmethod
def get_default_user_count():
"""Get current default user count"""
return LicenseKey.base_user_qs().filter(type=UserTypes.INTERNAL).count()
@staticmethod
def get_external_user_count():
"""Get current external user count"""
# Count since start of the month
last_month = now().replace(day=1)
return (
LicenseKey.base_user_qs()
.filter(type=UserTypes.EXTERNAL, last_login__gte=last_month)
.count()
)
def is_valid(self) -> bool:
"""Check if the given license body covers all users
Only checks the current count, no historical data is checked"""
default_users = self.get_default_user_count()
if default_users > self.users:
return False
active_users = self.get_external_user_count()
if active_users > self.external_users:
return False
return True
def record_usage(self):
"""Capture the current validity status and metrics and save them"""
LicenseUsage.objects.create(
user_count=self.get_default_user_count(),
external_user_count=self.get_external_user_count(),
within_limits=self.is_valid(),
)
@staticmethod
def last_valid_date() -> datetime:
"""Get the last date the license was valid"""
usage: LicenseUsage = (
LicenseUsage.filter_not_expired(within_limits=True).order_by("-record_date").first()
)
if not usage:
return now()
return usage.record_date
class License(models.Model):
"""An authentik enterprise license"""
license_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
key = models.TextField(unique=True)
name = models.TextField()
expiry = models.DateTimeField()
users = models.BigIntegerField()
external_users = models.BigIntegerField()
@property
def status(self) -> LicenseKey:
"""Get parsed license status"""
return LicenseKey.validate(self.key)
def usage_expiry():
"""Keep license usage records for 3 months"""
return now() + timedelta(days=30 * 3)
class LicenseUsage(ExpiringModel):
"""a single license usage record"""
expires = models.DateTimeField(default=usage_expiry)
usage_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
user_count = models.BigIntegerField()
external_user_count = models.BigIntegerField()
within_limits = models.BooleanField()
record_date = models.DateTimeField(auto_now_add=True)

View File

@ -0,0 +1,46 @@
"""Enterprise license policies"""
from typing import Optional
from rest_framework.serializers import BaseSerializer
from authentik.core.models import User, UserTypes
from authentik.enterprise.models import LicenseKey
from authentik.policies.models import Policy
from authentik.policies.types import PolicyRequest, PolicyResult
from authentik.policies.views import PolicyAccessView
class EnterprisePolicy(Policy):
"""Check that a user is correctly licensed for the request"""
@property
def component(self) -> str:
return ""
@property
def serializer(self) -> type[BaseSerializer]:
raise NotImplementedError
def passes(self, request: PolicyRequest) -> PolicyResult:
if not LicenseKey.get_total().is_valid():
return PolicyResult(False)
if request.user.type != UserTypes.INTERNAL:
return PolicyResult(False)
return PolicyResult(True)
class EnterprisePolicyAccessView(PolicyAccessView):
"""PolicyAccessView which also checks enterprise licensing"""
def user_has_access(self, user: Optional[User] = None) -> PolicyResult:
user = user or self.request.user
request = PolicyRequest(user)
request.http_request = self.request
result = super().user_has_access(user)
enterprise_result = EnterprisePolicy().passes(request)
if not enterprise_result.passing:
return enterprise_result
return result
def resolve_provider_application(self):
raise NotImplementedError

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEdzCCA/6gAwIBAgIUQrj1jxn4q/BB38B2SwTrvGyrZLMwCgYIKoZIzj0EAwMw
ge8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
YW4gRnJhbmNpc2NvMSQwIgYDVQQJExs1NDggTWFya2V0IFN0cmVldCBQbWIgNzAx
NDgxDjAMBgNVBBETBTk0MTA0MSAwHgYDVQQKExdBdXRoZW50aWsgU2VjdXJpdHkg
SW5jLjEcMBoGA1UECxMTRW50ZXJwcmlzZSBMaWNlbnNlczE9MDsGA1UEAxM0QXV0
aGVudGlrIFNlY3VyaXR5IEluYy4gRW50ZXJwcmlzZSBMaWNlbnNpbmcgUm9vdCBY
MTAgFw0yMzA3MDQxNzQ3NDBaGA8yMTIzMDYxMDE3NDgxMFowge8xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv
MSQwIgYDVQQJExs1NDggTWFya2V0IFN0cmVldCBQbWIgNzAxNDgxDjAMBgNVBBET
BTk0MTA0MSAwHgYDVQQKExdBdXRoZW50aWsgU2VjdXJpdHkgSW5jLjEcMBoGA1UE
CxMTRW50ZXJwcmlzZSBMaWNlbnNlczE9MDsGA1UEAxM0QXV0aGVudGlrIFNlY3Vy
aXR5IEluYy4gRW50ZXJwcmlzZSBMaWNlbnNpbmcgUm9vdCBYMTB2MBAGByqGSM49
AgEGBSuBBAAiA2IABNbPJH6nDbSshpDsDHBRL0UcZVXWCK30txqcMKU+YFmLB6iR
PJiHjHA8Z+5aP4eNH6onA5xqykQf65tvbFBA1LB/6HqMArU/tYVVQx4+o9hRBxF5
RrzXucUg2br+RX8aa6OCAVUwggFRMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRHpR3/ptPgN0yHVfUjyJOEmsPZqTAfBgNVHSMEGDAW
gBRHpR3/ptPgN0yHVfUjyJOEmsPZqTCBoAYIKwYBBQUHAQEEgZMwgZAwRwYIKwYB
BQUHMAGGO2h0dHBzOi8vdmF1bHQuY3VzdG9tZXJzLmdvYXV0aGVudGlrLmlvL3Yx
L2xpY2Vuc2luZy1jYS9vY3NwMEUGCCsGAQUFBzAChjlodHRwczovL3ZhdWx0LmN1
c3RvbWVycy5nb2F1dGhlbnRpay5pby92MS9saWNlbnNpbmctY2EvY2EwSwYDVR0f
BEQwQjBAoD6gPIY6aHR0cHM6Ly92YXVsdC5jdXN0b21lcnMuZ29hdXRoZW50aWsu
aW8vdjEvbGljZW5zaW5nLWNhL2NybDAKBggqhkjOPQQDAwNnADBkAjB0+YA1yjEO
g43CCYUJXz9m9CNIkjOPUI0jO4UtvSj8j067TKRbX6IL/29HxPtQoYACME8eZHBJ
Ljcog0oeBgjr4wK8bobgknr5wrm70rrNNpbSAjDvTvXMQeAShGgsftEquQ==
-----END CERTIFICATE-----

View File

@ -1 +1,12 @@
"""Enterprise additional settings"""
from celery.schedules import crontab
from authentik.lib.utils.time import fqdn_rand
CELERY_BEAT_SCHEDULE = {
"enterprise_calculate_license": {
"task": "authentik.enterprise.tasks.calculate_license",
"schedule": crontab(minute=fqdn_rand("calculate_license"), hour="*/8"),
"options": {"queue": "authentik_scheduled"},
}
}

View File

@ -0,0 +1,18 @@
"""Enterprise signals"""
from datetime import datetime
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.timezone import get_current_timezone
from authentik.enterprise.models import License
@receiver(pre_save, sender=License)
def pre_save_license(sender: type[License], instance: License, **_):
"""Extract data from license jwt and save it into model"""
status = instance.status
instance.name = status.name
instance.users = status.users
instance.external_users = status.external_users
instance.expiry = datetime.fromtimestamp(status.exp, tz=get_current_timezone())

View File

@ -0,0 +1,10 @@
"""Enterprise tasks"""
from authentik.enterprise.models import LicenseKey
from authentik.root.celery import CELERY_APP
@CELERY_APP.task()
def calculate_license():
"""Calculate licensing status"""
total = LicenseKey.get_total()
total.record_usage()

View File

View File

@ -0,0 +1,64 @@
"""Enterprise license tests"""
from datetime import timedelta
from time import mktime
from unittest.mock import MagicMock, patch
from django.test import TestCase
from django.utils.timezone import now
from rest_framework.exceptions import ValidationError
from authentik.enterprise.models import License, LicenseKey
from authentik.lib.generators import generate_id
_exp = int(mktime((now() + timedelta(days=3000)).timetuple()))
class TestEnterpriseLicense(TestCase):
"""Enterprise license tests"""
@patch(
"authentik.enterprise.models.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=_exp,
name=generate_id(),
users=100,
external_users=100,
)
),
)
def test_valid(self):
"""Check license verification"""
lic = License.objects.create(key=generate_id())
self.assertTrue(lic.status.is_valid())
self.assertEqual(lic.users, 100)
def test_invalid(self):
"""Test invalid license"""
with self.assertRaises(ValidationError):
License.objects.create(key=generate_id())
@patch(
"authentik.enterprise.models.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=_exp,
name=generate_id(),
users=100,
external_users=100,
)
),
)
def test_valid_multiple(self):
"""Check license verification"""
lic = License.objects.create(key=generate_id())
self.assertTrue(lic.status.is_valid())
lic2 = License.objects.create(key=generate_id())
self.assertTrue(lic2.status.is_valid())
total = LicenseKey.get_total()
self.assertEqual(total.users, 200)
self.assertEqual(total.external_users, 200)
self.assertEqual(total.exp, _exp)
self.assertTrue(total.is_valid())

View File

@ -0,0 +1,7 @@
"""API URLs"""
from authentik.enterprise.api import LicenseViewSet
api_urlpatterns = [
("enterprise/license", LicenseViewSet),
]

View File

@ -33,7 +33,7 @@ class GeoIPReader:
def __open(self):
"""Get GeoIP Reader, if configured, otherwise none"""
path = CONFIG.y("geoip")
path = CONFIG.get("geoip")
if path == "" or not path:
return
try:
@ -46,7 +46,7 @@ class GeoIPReader:
def __check_expired(self):
"""Check if the modification date of the GeoIP database has
changed, and reload it if so"""
path = CONFIG.y("geoip")
path = CONFIG.get("geoip")
try:
mtime = stat(path).st_mtime
diff = self.__last_mtime < mtime

View File

@ -76,9 +76,20 @@ class TaskInfo:
return cache.get_many(cache.keys(CACHE_KEY_PREFIX + name)).values()
return cache.get(CACHE_KEY_PREFIX + name, None)
@property
def full_name(self) -> str:
"""Get the full cache key with task name and UID"""
key = CACHE_KEY_PREFIX + self.task_name
if self.result.uid:
uid_suffix = f":{self.result.uid}"
key += uid_suffix
if not self.task_name.endswith(uid_suffix):
self.task_name += uid_suffix
return key
def delete(self):
"""Delete task info from cache"""
return cache.delete(CACHE_KEY_PREFIX + self.task_name)
return cache.delete(self.full_name)
def update_metrics(self):
"""Update prometheus metrics"""
@ -92,17 +103,13 @@ class TaskInfo:
GAUGE_TASKS.labels(
task_name=self.task_name.split(":")[0],
task_uid=self.result.uid or "",
status=self.result.status.value,
status=self.result.status.name.lower(),
).set(duration)
def save(self, timeout_hours=6):
"""Save task into cache"""
key = CACHE_KEY_PREFIX + self.task_name
if self.result.uid:
key += f":{self.result.uid}"
self.task_name += f":{self.result.uid}"
self.update_metrics()
cache.set(key, self, timeout=timeout_hours * 60 * 60)
cache.set(self.full_name, self, timeout=timeout_hours * 60 * 60)
class MonitoredTask(Task):

View File

@ -0,0 +1,43 @@
"""Test Monitored tasks"""
from django.test import TestCase
from authentik.events.monitored_tasks import MonitoredTask, TaskInfo, TaskResult, TaskResultStatus
from authentik.lib.generators import generate_id
from authentik.root.celery import CELERY_APP
class TestMonitoredTasks(TestCase):
"""Test Monitored tasks"""
def test_failed_successful_remove_state(self):
"""Test that a task with `save_on_success` set to `False` that failed saves
a state, and upon successful completion will delete the state"""
should_fail = True
uid = generate_id()
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
)
def test_task(self: MonitoredTask):
self.save_on_success = False
self.set_uid(uid)
self.set_status(
TaskResult(TaskResultStatus.ERROR if should_fail else TaskResultStatus.SUCCESSFUL)
)
# First test successful run
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))
# Then test failed
should_fail = True
test_task.delay().get()
info = TaskInfo.by_name(f"test_task:{uid}")
self.assertEqual(info.result.status, TaskResultStatus.ERROR)
# Then after that, the state should be removed
should_fail = False
test_task.delay().get()
self.assertIsNone(TaskInfo.by_name(f"test_task:{uid}"))

View File

@ -33,7 +33,7 @@ PLAN_CONTEXT_SOURCE = "source"
# Is set by the Flow Planner when a FlowToken was used, and the currently active flow plan
# was restored.
PLAN_CONTEXT_IS_RESTORED = "is_restored"
CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_flows"))
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_flows"))
CACHE_PREFIX = "goauthentik.io/flows/planner/"

View File

@ -18,7 +18,6 @@ from authentik.flows.planner import FlowPlan, FlowPlanner
from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
@ -85,7 +84,6 @@ class TestFlowExecutor(FlowTestCase):
FlowDesignation.AUTHENTICATION,
)
CONFIG.update_from_dict({"domain": "testserver"})
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
@ -111,7 +109,6 @@ class TestFlowExecutor(FlowTestCase):
denied_action=FlowDeniedAction.CONTINUE,
)
CONFIG.update_from_dict({"domain": "testserver"})
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
@ -128,7 +125,6 @@ class TestFlowExecutor(FlowTestCase):
FlowDesignation.AUTHENTICATION,
)
CONFIG.update_from_dict({"domain": "testserver"})
dest = "/unique-string"
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug})
response = self.client.get(url + f"?{NEXT_ARG_NAME}={dest}")
@ -145,7 +141,6 @@ class TestFlowExecutor(FlowTestCase):
FlowDesignation.AUTHENTICATION,
)
CONFIG.update_from_dict({"domain": "testserver"})
response = self.client.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)

View File

@ -1,10 +0,0 @@
"""authentik lib app config"""
from django.apps import AppConfig
class AuthentikLibConfig(AppConfig):
"""authentik lib app config"""
name = "authentik.lib"
label = "authentik_lib"
verbose_name = "authentik lib"

View File

@ -175,7 +175,7 @@ def get_avatar(user: "User") -> str:
"initials": avatar_mode_generated,
"gravatar": avatar_mode_gravatar,
}
modes: str = CONFIG.y("avatars", "none")
modes: str = CONFIG.get("avatars", "none")
for mode in modes.split(","):
avatar = None
if mode in mode_map:

View File

@ -2,13 +2,15 @@
import os
from collections.abc import Mapping
from contextlib import contextmanager
from dataclasses import dataclass, field
from enum import Enum
from glob import glob
from json import dumps, loads
from json import JSONEncoder, dumps, loads
from json.decoder import JSONDecodeError
from pathlib import Path
from sys import argv, stderr
from time import time
from typing import Any
from typing import Any, Optional
from urllib.parse import urlparse
import yaml
@ -32,15 +34,44 @@ def get_path_from_dict(root: dict, path: str, sep=".", default=None) -> Any:
return root
@dataclass
class Attr:
"""Single configuration attribute"""
class Source(Enum):
"""Sources a configuration attribute can come from, determines what should be done with
Attr.source (and if it's set at all)"""
UNSPECIFIED = "unspecified"
ENV = "env"
CONFIG_FILE = "config_file"
URI = "uri"
value: Any
source_type: Source = field(default=Source.UNSPECIFIED)
# depending on source_type, might contain the environment variable or the path
# to the config file containing this change or the file containing this value
source: Optional[str] = field(default=None)
class AttrEncoder(JSONEncoder):
"""JSON encoder that can deal with `Attr` classes"""
def default(self, o: Any) -> Any:
if isinstance(o, Attr):
return o.value
return super().default(o)
class ConfigLoader:
"""Search through SEARCH_PATHS and load configuration. Environment variables starting with
`ENV_PREFIX` are also applied.
A variable like AUTHENTIK_POSTGRESQL__HOST would translate to postgresql.host"""
loaded_file = []
def __init__(self):
def __init__(self, **kwargs):
super().__init__()
self.__config = {}
base_dir = Path(__file__).parent.joinpath(Path("../..")).resolve()
@ -65,6 +96,7 @@ class ConfigLoader:
# Update config with env file
self.update_from_file(env_file)
self.update_from_env()
self.update(self.__config, kwargs)
def log(self, level: str, message: str, **kwargs):
"""Custom Log method, we want to ensure ConfigLoader always logs JSON even when
@ -86,22 +118,34 @@ class ConfigLoader:
else:
if isinstance(value, str):
value = self.parse_uri(value)
elif isinstance(value, Attr) and isinstance(value.value, str):
value = self.parse_uri(value.value)
elif not isinstance(value, Attr):
value = Attr(value)
root[key] = value
return root
def parse_uri(self, value: str) -> str:
def refresh(self, key: str):
"""Update a single value"""
attr: Attr = get_path_from_dict(self.raw, key)
if attr.source_type != Attr.Source.URI:
return
attr.value = self.parse_uri(attr.source).value
def parse_uri(self, value: str) -> Attr:
"""Parse string values which start with a URI"""
url = urlparse(value)
parsed_value = value
if url.scheme == "env":
value = os.getenv(url.netloc, url.query)
parsed_value = os.getenv(url.netloc, url.query)
if url.scheme == "file":
try:
with open(url.path, "r", encoding="utf8") as _file:
value = _file.read().strip()
parsed_value = _file.read().strip()
except OSError as exc:
self.log("error", f"Failed to read config value from {url.path}: {exc}")
value = url.query
return value
parsed_value = url.query
return Attr(parsed_value, Attr.Source.URI, value)
def update_from_file(self, path: Path):
"""Update config from file contents"""
@ -110,7 +154,6 @@ class ConfigLoader:
try:
self.update(self.__config, yaml.safe_load(file))
self.log("debug", "Loaded config", file=str(path))
self.loaded_file.append(path)
except yaml.YAMLError as exc:
raise ImproperlyConfigured from exc
except PermissionError as exc:
@ -121,10 +164,6 @@ class ConfigLoader:
error=str(exc),
)
def update_from_dict(self, update: dict):
"""Update config from dict"""
self.__config.update(update)
def update_from_env(self):
"""Check environment variables"""
outer = {}
@ -145,7 +184,7 @@ class ConfigLoader:
value = loads(value)
except JSONDecodeError:
pass
current_obj[dot_parts[-1]] = value
current_obj[dot_parts[-1]] = Attr(value, Attr.Source.ENV, key)
idx += 1
if idx > 0:
self.log("debug", "Loaded environment variables", count=idx)
@ -154,28 +193,32 @@ class ConfigLoader:
@contextmanager
def patch(self, path: str, value: Any):
"""Context manager for unittests to patch a value"""
original_value = self.y(path)
self.y_set(path, value)
original_value = self.get(path)
self.set(path, value)
try:
yield
finally:
self.y_set(path, original_value)
self.set(path, original_value)
@property
def raw(self) -> dict:
"""Get raw config dictionary"""
return self.__config
# pylint: disable=invalid-name
def y(self, path: str, default=None, sep=".") -> Any:
def get(self, path: str, default=None, sep=".") -> Any:
"""Access attribute by using yaml path"""
# Walk sub_dicts before parsing path
root = self.raw
# Walk each component of the path
return get_path_from_dict(root, path, sep=sep, default=default)
attr: Attr = get_path_from_dict(root, path, sep=sep, default=Attr(default))
return attr.value
def y_set(self, path: str, value: Any, sep="."):
"""Set value using same syntax as y()"""
def get_bool(self, path: str, default=False) -> bool:
"""Wrapper for get that converts value into boolean"""
return str(self.get(path, default)).lower() == "true"
def set(self, path: str, value: Any, sep="."):
"""Set value using same syntax as get()"""
# Walk sub_dicts before parsing path
root = self.raw
# Walk each component of the path
@ -184,17 +227,14 @@ class ConfigLoader:
if comp not in root:
root[comp] = {}
root = root.get(comp, {})
root[path_parts[-1]] = value
def y_bool(self, path: str, default=False) -> bool:
"""Wrapper for y that converts value into boolean"""
return str(self.y(path, default)).lower() == "true"
root[path_parts[-1]] = Attr(value)
CONFIG = ConfigLoader()
if __name__ == "__main__":
if len(argv) < 2:
print(dumps(CONFIG.raw, indent=4))
print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder))
else:
print(CONFIG.y(argv[1]))
print(CONFIG.get(argv[1]))

View File

@ -51,18 +51,18 @@ class SentryTransport(HttpTransport):
def sentry_init(**sentry_init_kwargs):
"""Configure sentry SDK"""
sentry_env = CONFIG.y("error_reporting.environment", "customer")
sentry_env = CONFIG.get("error_reporting.environment", "customer")
kwargs = {
"environment": sentry_env,
"send_default_pii": CONFIG.y_bool("error_reporting.send_pii", False),
"send_default_pii": CONFIG.get_bool("error_reporting.send_pii", False),
"_experiments": {
"profiles_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.1)),
"profiles_sample_rate": float(CONFIG.get("error_reporting.sample_rate", 0.1)),
},
}
kwargs.update(**sentry_init_kwargs)
# pylint: disable=abstract-class-instantiated
sentry_sdk_init(
dsn=CONFIG.y("error_reporting.sentry_dsn"),
dsn=CONFIG.get("error_reporting.sentry_dsn"),
integrations=[
ArgvIntegration(),
StdlibIntegration(),
@ -92,7 +92,7 @@ def traces_sampler(sampling_context: dict) -> float:
return 0
if _type == "websocket":
return 0
return float(CONFIG.y("error_reporting.sample_rate", 0.1))
return float(CONFIG.get("error_reporting.sample_rate", 0.1))
def before_send(event: dict, hint: dict) -> Optional[dict]:

View File

@ -16,23 +16,23 @@ class TestConfig(TestCase):
config = ConfigLoader()
environ[ENV_PREFIX + "_test__test"] = "bar"
config.update_from_env()
self.assertEqual(config.y("test.test"), "bar")
self.assertEqual(config.get("test.test"), "bar")
def test_patch(self):
"""Test patch decorator"""
config = ConfigLoader()
config.y_set("foo.bar", "bar")
self.assertEqual(config.y("foo.bar"), "bar")
config.set("foo.bar", "bar")
self.assertEqual(config.get("foo.bar"), "bar")
with config.patch("foo.bar", "baz"):
self.assertEqual(config.y("foo.bar"), "baz")
self.assertEqual(config.y("foo.bar"), "bar")
self.assertEqual(config.get("foo.bar"), "baz")
self.assertEqual(config.get("foo.bar"), "bar")
def test_uri_env(self):
"""Test URI parsing (environment)"""
config = ConfigLoader()
environ["foo"] = "bar"
self.assertEqual(config.parse_uri("env://foo"), "bar")
self.assertEqual(config.parse_uri("env://foo?bar"), "bar")
self.assertEqual(config.parse_uri("env://foo").value, "bar")
self.assertEqual(config.parse_uri("env://foo?bar").value, "bar")
def test_uri_file(self):
"""Test URI parsing (file load)"""
@ -41,11 +41,31 @@ class TestConfig(TestCase):
write(file, "foo".encode())
_, file2_name = mkstemp()
chmod(file2_name, 0o000) # Remove all permissions so we can't read the file
self.assertEqual(config.parse_uri(f"file://{file_name}"), "foo")
self.assertEqual(config.parse_uri(f"file://{file2_name}?def"), "def")
self.assertEqual(config.parse_uri(f"file://{file_name}").value, "foo")
self.assertEqual(config.parse_uri(f"file://{file2_name}?def").value, "def")
unlink(file_name)
unlink(file2_name)
def test_uri_file_update(self):
"""Test URI parsing (file load and update)"""
file, file_name = mkstemp()
write(file, "foo".encode())
config = ConfigLoader(file_test=f"file://{file_name}")
self.assertEqual(config.get("file_test"), "foo")
# Update config file
write(file, "bar".encode())
config.refresh("file_test")
self.assertEqual(config.get("file_test"), "foobar")
unlink(file_name)
def test_uri_env_full(self):
"""Test URI set as env variable"""
environ["AUTHENTIK_TEST_VAR"] = "file:///foo?bar"
config = ConfigLoader()
self.assertEqual(config.get("test_var"), "bar")
def test_file_update(self):
"""Test update_from_file"""
config = ConfigLoader()

View File

@ -1,7 +1,7 @@
"""Test HTTP Helpers"""
from django.test import RequestFactory, TestCase
from authentik.core.models import USER_ATTRIBUTE_CAN_OVERRIDE_IP, Token, TokenIntents
from authentik.core.models import Token, TokenIntents, UserTypes
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.utils.http import OUTPOST_REMOTE_IP_HEADER, OUTPOST_TOKEN_HEADER, get_client_ip
from authentik.lib.views import bad_request_message
@ -53,7 +53,7 @@ class TestHTTP(TestCase):
)
self.assertEqual(get_client_ip(request), "127.0.0.1")
# Valid
self.user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
self.user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
self.user.save()
request = self.factory.get(
"/",

View File

@ -33,9 +33,8 @@ def _get_client_ip_from_meta(meta: dict[str, Any]) -> str:
def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
"""Get the actual remote IP when set by an outpost. Only
allowed when the request is authenticated, by a user with USER_ATTRIBUTE_CAN_OVERRIDE_IP set
to outpost"""
from authentik.core.models import USER_ATTRIBUTE_CAN_OVERRIDE_IP, Token, TokenIntents
allowed when the request is authenticated, by an outpost internal service account"""
from authentik.core.models import Token, TokenIntents, UserTypes
if OUTPOST_REMOTE_IP_HEADER not in request.META or OUTPOST_TOKEN_HEADER not in request.META:
return None
@ -51,7 +50,7 @@ def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
LOGGER.warning("Attempted remote-ip override without token", fake_ip=fake_ip)
return None
user = token.user
if not user.group_attributes(request).get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
if user.type != UserTypes.INTERNAL_SERVICE_ACCOUNT:
LOGGER.warning(
"Remote-IP override: user doesn't have permission",
user=user,

View File

@ -50,7 +50,7 @@ def get_env() -> str:
"""Get environment in which authentik is currently running"""
if "CI" in os.environ:
return "ci"
if CONFIG.y_bool("debug"):
if CONFIG.get_bool("debug"):
return "dev"
if SERVICE_HOST_ENV_NAME in os.environ:
return "kubernetes"

View File

@ -97,7 +97,7 @@ class BaseController:
if self.outpost.config.container_image is not None:
return self.outpost.config.container_image
image_name_template: str = CONFIG.y("outposts.container_image_base")
image_name_template: str = CONFIG.get("outposts.container_image_base")
return image_name_template % {
"type": self.outpost.type,
"version": __version__,

View File

@ -1,16 +1,22 @@
"""Base Kubernetes Reconciler"""
from dataclasses import asdict
from json import dumps
from typing import TYPE_CHECKING, Generic, Optional, TypeVar
from dacite.core import from_dict
from django.utils.text import slugify
from kubernetes.client import V1ObjectMeta
from jsonpatch import JsonPatchConflict, JsonPatchException, JsonPatchTestFailed, apply_patch
from kubernetes.client import ApiClient, V1ObjectMeta
from kubernetes.client.exceptions import ApiException, OpenApiException
from kubernetes.client.models.v1_deployment import V1Deployment
from kubernetes.client.models.v1_pod import V1Pod
from requests import Response
from structlog.stdlib import get_logger
from urllib3.exceptions import HTTPError
from authentik import __version__
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import ControllerException
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate, NeedsUpdate
if TYPE_CHECKING:
@ -34,11 +40,23 @@ class KubernetesObjectReconciler(Generic[T]):
self.namespace = controller.outpost.config.kubernetes_namespace
self.logger = get_logger().bind(type=self.__class__.__name__)
def get_patch(self):
"""Get any patches that apply to this CRD"""
patches = self.controller.outpost.config.kubernetes_json_patches
if not patches:
return None
return patches.get(self.reconciler_name(), None)
@property
def is_embedded(self) -> bool:
"""Return true if the current outpost is embedded"""
return self.controller.outpost.managed == MANAGED_OUTPOST
@staticmethod
def reconciler_name() -> str:
"""A name this reconciler is identified by in the configuration"""
raise NotImplementedError
@property
def noop(self) -> bool:
"""Return true if this object should not be created/updated/deleted in this cluster"""
@ -55,6 +73,32 @@ class KubernetesObjectReconciler(Generic[T]):
}
).lower()
def get_patched_reference_object(self) -> T:
"""Get patched reference object"""
reference = self.get_reference_object()
patch = self.get_patch()
try:
json = ApiClient().sanitize_for_serialization(reference)
# Custom objects will not be known to the clients openapi types
except AttributeError:
json = asdict(reference)
try:
ref = json
if patch is not None:
ref = apply_patch(json, patch)
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
raise ControllerException(f"JSON Patch failed: {exc}") from exc
mock_response = Response()
mock_response.data = dumps(ref)
try:
result = ApiClient().deserialize(mock_response, reference.__class__.__name__)
# Custom objects will not be known to the clients openapi types
except AttributeError:
result = from_dict(reference.__class__, data=ref)
return result
# pylint: disable=invalid-name
def up(self):
"""Create object if it doesn't exist, update if needed or recreate if needed."""
@ -62,7 +106,7 @@ class KubernetesObjectReconciler(Generic[T]):
if self.noop:
self.logger.debug("Object is noop")
return
reference = self.get_reference_object()
reference = self.get_patched_reference_object()
try:
try:
current = self.retrieve()
@ -129,6 +173,16 @@ class KubernetesObjectReconciler(Generic[T]):
if current.metadata.labels != reference.metadata.labels:
raise NeedsUpdate()
patch = self.get_patch()
if patch is not None:
current_json = ApiClient().sanitize_for_serialization(current)
try:
if apply_patch(current_json, patch) != current_json:
raise NeedsUpdate()
except (JsonPatchException, JsonPatchConflict, JsonPatchTestFailed) as exc:
raise ControllerException(f"JSON Patch failed: {exc}") from exc
def create(self, reference: T):
"""API Wrapper to create object"""
raise NotImplementedError

View File

@ -43,6 +43,10 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
self.api = AppsV1Api(controller.client)
self.outpost = self.controller.outpost
@staticmethod
def reconciler_name() -> str:
return "deployment"
def reconcile(self, current: V1Deployment, reference: V1Deployment):
compare_ports(
current.spec.template.spec.containers[0].ports,

View File

@ -24,6 +24,10 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "secret"
def reconcile(self, current: V1Secret, reference: V1Secret):
super().reconcile(current, reference)
for key in reference.data.keys():

View File

@ -20,6 +20,10 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
super().__init__(controller)
self.api = CoreV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "service"
def reconcile(self, current: V1Service, reference: V1Service):
compare_ports(current.spec.ports, reference.spec.ports)
# run the base reconcile last, as that will probably raise NeedsUpdate

View File

@ -71,6 +71,10 @@ class PrometheusServiceMonitorReconciler(KubernetesObjectReconciler[PrometheusSe
self.api_ex = ApiextensionsV1Api(controller.client)
self.api = CustomObjectsApi(controller.client)
@staticmethod
def reconciler_name() -> str:
return "prometheus servicemonitor"
@property
def noop(self) -> bool:
return (not self._crd_exists()) or (self.is_embedded)

View File

@ -64,12 +64,19 @@ class KubernetesController(BaseController):
super().__init__(outpost, connection)
self.client = KubernetesClient(connection)
self.reconcilers = {
"secret": SecretReconciler,
"deployment": DeploymentReconciler,
"service": ServiceReconciler,
"prometheus servicemonitor": PrometheusServiceMonitorReconciler,
SecretReconciler.reconciler_name(): SecretReconciler,
DeploymentReconciler.reconciler_name(): DeploymentReconciler,
ServiceReconciler.reconciler_name(): ServiceReconciler,
PrometheusServiceMonitorReconciler.reconciler_name(): (
PrometheusServiceMonitorReconciler
),
}
self.reconcile_order = ["secret", "deployment", "service", "prometheus servicemonitor"]
self.reconcile_order = [
SecretReconciler.reconciler_name(),
DeploymentReconciler.reconciler_name(),
ServiceReconciler.reconciler_name(),
PrometheusServiceMonitorReconciler.reconciler_name(),
]
def up(self):
try:

View File

@ -1,7 +1,7 @@
"""Outpost models"""
from dataclasses import asdict, dataclass, field
from datetime import datetime
from typing import Iterable, Optional
from typing import Any, Iterable, Optional
from uuid import uuid4
from dacite.core import from_dict
@ -20,13 +20,12 @@ from structlog.stdlib import get_logger
from authentik import __version__, get_build_hash
from authentik.blueprints.models import ManagedModel
from authentik.core.models import (
USER_ATTRIBUTE_CAN_OVERRIDE_IP,
USER_ATTRIBUTE_SA,
USER_PATH_SYSTEM_PREFIX,
Provider,
Token,
TokenIntents,
User,
UserTypes,
)
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
@ -59,7 +58,7 @@ class OutpostConfig:
authentik_host_insecure: bool = False
authentik_host_browser: str = ""
log_level: str = CONFIG.y("log_level")
log_level: str = CONFIG.get("log_level")
object_naming_template: str = field(default="ak-outpost-%(name)s")
container_image: Optional[str] = field(default=None)
@ -76,6 +75,7 @@ class OutpostConfig:
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
kubernetes_image_pull_secrets: list[str] = field(default_factory=list)
kubernetes_json_patches: Optional[dict[str, list[dict[str, Any]]]] = field(default=None)
class OutpostModel(Model):
@ -346,8 +346,7 @@ class Outpost(SerializerModel, ManagedModel):
user: User = User.objects.create(username=self.user_identifier)
user.set_unusable_password()
user_created = True
user.attributes[USER_ATTRIBUTE_SA] = True
user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
user.type = UserTypes.INTERNAL_SERVICE_ACCOUNT
user.name = f"Outpost {self.name} Service-Account"
user.path = USER_PATH_OUTPOSTS
user.save()

View File

@ -256,7 +256,7 @@ def _outpost_single_update(outpost: Outpost, layer=None):
def outpost_connection_discovery(self: MonitoredTask):
"""Checks the local environment and create Service connections."""
status = TaskResult(TaskResultStatus.SUCCESSFUL)
if not CONFIG.y_bool("outposts.discover"):
if not CONFIG.get_bool("outposts.discover"):
status.messages.append("Outpost integration discovery is disabled")
self.set_status(status)
return

View File

@ -64,7 +64,7 @@ class PolicyEngine:
self.use_cache = True
self.__expected_result_count = 0
def _iter_bindings(self) -> Iterator[PolicyBinding]:
def iterate_bindings(self) -> Iterator[PolicyBinding]:
"""Make sure all Policies are their respective classes"""
return (
PolicyBinding.objects.filter(target=self.__pbm, enabled=True)
@ -88,7 +88,7 @@ class PolicyEngine:
span: Span
span.set_data("pbm", self.__pbm)
span.set_data("request", self.request)
for binding in self._iter_bindings():
for binding in self.iterate_bindings():
self.__expected_result_count += 1
self._check_policy_type(binding)

View File

@ -19,7 +19,7 @@ from authentik.policies.types import CACHE_PREFIX, PolicyRequest, PolicyResult
LOGGER = get_logger()
FORK_CTX = get_context("fork")
CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_policies"))
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_policies"))
PROCESS_CLASS = FORK_CTX.Process

View File

@ -13,7 +13,7 @@ from authentik.policies.reputation.tasks import save_reputation
from authentik.stages.identification.signals import identification_failed
LOGGER = get_logger()
CACHE_TIMEOUT = int(CONFIG.y("redis.cache_timeout_reputation"))
CACHE_TIMEOUT = int(CONFIG.get("redis.cache_timeout_reputation"))
def update_score(request: HttpRequest, identifier: str, amount: int):

View File

@ -6,11 +6,12 @@ from django.urls import reverse
from jwt import decode
from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Group, Token, TokenIntents
from authentik.core.models import Application, Group, Token, TokenIntents, UserTypes
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_PASSWORD,
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
@ -37,7 +38,7 @@ class TestTokenClientCredentials(OAuthTestCase):
self.provider.property_mappings.set(ScopeMapping.objects.all())
self.app = Application.objects.create(name="test", slug="test", provider=self.provider)
self.user = create_test_admin_user("sa")
self.user.attributes[USER_ATTRIBUTE_SA] = True
self.user.type = UserTypes.SERVICE_ACCOUNT
self.user.save()
self.token = Token.objects.create(
identifier="sa-token",
@ -150,3 +151,28 @@ class TestTokenClientCredentials(OAuthTestCase):
)
self.assertEqual(jwt["given_name"], self.user.name)
self.assertEqual(jwt["preferred_username"], self.user.username)
def test_successful_password(self):
"""test successful (password grant)"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_PASSWORD,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"username": "sa",
"password": self.token.key,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
self.assertEqual(body["token_type"], TOKEN_TYPE)
_, alg = self.provider.jwt_key
jwt = decode(
body["access_token"],
key=self.provider.signing_key.public_key,
algorithms=[alg],
audience=self.provider.client_id,
)
self.assertEqual(jwt["given_name"], self.user.name)
self.assertEqual(jwt["preferred_username"], self.user.username)

View File

@ -46,7 +46,7 @@ class DeviceView(View):
def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
throttle = AnonRateThrottle()
throttle.rate = CONFIG.y("throttle.providers.oauth2.device", "20/hour")
throttle.rate = CONFIG.get("throttle.providers.oauth2.device", "20/hour")
throttle.num_requests, throttle.duration = throttle.parse_rate(throttle.rate)
if not throttle.allow_request(request, self):
return HttpResponse(status=429)

View File

@ -459,13 +459,13 @@ class TokenView(View):
if self.params.grant_type == GRANT_TYPE_REFRESH_TOKEN:
LOGGER.debug("Refreshing refresh token")
return TokenResponse(self.create_refresh_response())
if self.params.grant_type == GRANT_TYPE_CLIENT_CREDENTIALS:
LOGGER.debug("Client credentials grant")
if self.params.grant_type in [GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_PASSWORD]:
LOGGER.debug("Client credentials/password grant")
return TokenResponse(self.create_client_credentials_response())
if self.params.grant_type == GRANT_TYPE_DEVICE_CODE:
LOGGER.debug("Device code grant")
return TokenResponse(self.create_device_code_response())
raise ValueError(f"Invalid grant_type: {self.params.grant_type}")
raise TokenError("unsupported_grant_type")
except (TokenError, DeviceCodeError) as error:
return TokenResponse(error.create_dict(), status=400)
except UserAuthError as error:

View File

@ -31,6 +31,10 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
super().__init__(controller)
self.api = NetworkingV1Api(controller.client)
@staticmethod
def reconciler_name() -> str:
return "ingress"
def _check_annotations(self, reference: V1Ingress):
"""Check that all annotations *we* set are correct"""
for key, value in self.get_ingress_annotations().items():

View File

@ -17,24 +17,28 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler):
if not self.reconciler.crd_exists():
self.reconciler = Traefik2MiddlewareReconciler(controller)
@staticmethod
def reconciler_name() -> str:
return "traefik middleware"
@property
def noop(self) -> bool:
return self.reconciler.noop
def reconcile(self, current: TraefikMiddleware, reference: TraefikMiddleware):
return self.reconcile(current, reference)
return self.reconciler.reconcile(current, reference)
def get_reference_object(self) -> TraefikMiddleware:
return self.get_reference_object()
return self.reconciler.get_reference_object()
def create(self, reference: TraefikMiddleware):
return self.create(reference)
return self.reconciler.create(reference)
def delete(self, reference: TraefikMiddleware):
return self.delete(reference)
return self.reconciler.delete(reference)
def retrieve(self) -> TraefikMiddleware:
return self.retrieve()
return self.reconciler.retrieve()
def update(self, current: TraefikMiddleware, reference: TraefikMiddleware):
return self.update(current, reference)
return self.reconciler.update(current, reference)

View File

@ -67,6 +67,10 @@ class Traefik3MiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware]
self.crd_version = "v1alpha1"
self.crd_plural = "middlewares"
@staticmethod
def reconciler_name() -> str:
return "traefik middleware"
@property
def noop(self) -> bool:
if not ProxyProvider.objects.filter(

View File

@ -16,7 +16,9 @@ class ProxyKubernetesController(KubernetesController):
DeploymentPort(9300, "http-metrics", "tcp"),
DeploymentPort(9443, "https", "tcp"),
]
self.reconcilers["ingress"] = IngressReconciler
self.reconcilers["traefik middleware"] = TraefikMiddlewareReconciler
self.reconcile_order.append("ingress")
self.reconcile_order.append("traefik middleware")
self.reconcilers[IngressReconciler.reconciler_name()] = IngressReconciler
self.reconcilers[
TraefikMiddlewareReconciler.reconciler_name()
] = TraefikMiddlewareReconciler
self.reconcile_order.append(IngressReconciler.reconciler_name())
self.reconcile_order.append(TraefikMiddlewareReconciler.reconciler_name())

View File

@ -1,17 +1,11 @@
"""SCIM Provider models"""
from django.db import models
from django.db.models import Q, QuerySet
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from guardian.shortcuts import get_anonymous_user
from rest_framework.serializers import Serializer
from authentik.core.models import (
USER_ATTRIBUTE_SA,
BackchannelProvider,
Group,
PropertyMapping,
User,
)
from authentik.core.models import BackchannelProvider, Group, PropertyMapping, User, UserTypes
class SCIMProvider(BackchannelProvider):
@ -38,17 +32,8 @@ class SCIMProvider(BackchannelProvider):
according to the provider's settings"""
base = User.objects.all().exclude(pk=get_anonymous_user().pk)
if self.exclude_users_service_account:
base = base.filter(
Q(
**{
f"attributes__{USER_ATTRIBUTE_SA}__isnull": True,
}
)
| Q(
**{
f"attributes__{USER_ATTRIBUTE_SA}": False,
}
)
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])

View File

15
authentik/root/db/base.py Normal file
View File

@ -0,0 +1,15 @@
"""authentik database backend"""
from django_prometheus.db.backends.postgresql.base import DatabaseWrapper as BaseDatabaseWrapper
from authentik.lib.config import CONFIG
class DatabaseWrapper(BaseDatabaseWrapper):
"""database backend which supports rotating credentials"""
def get_connection_params(self):
CONFIG.refresh("postgresql.password")
conn_params = super().get_connection_params()
conn_params["user"] = CONFIG.get("postgresql.user")
conn_params["password"] = CONFIG.get("postgresql.password")
return conn_params

View File

@ -26,15 +26,15 @@ def get_install_id_raw():
"""Get install_id without django loaded, this is required for the startup when we get
the install_id but django isn't loaded yet and we can't use the function above."""
conn = connect(
dbname=CONFIG.y("postgresql.name"),
user=CONFIG.y("postgresql.user"),
password=CONFIG.y("postgresql.password"),
host=CONFIG.y("postgresql.host"),
port=int(CONFIG.y("postgresql.port")),
sslmode=CONFIG.y("postgresql.sslmode"),
sslrootcert=CONFIG.y("postgresql.sslrootcert"),
sslcert=CONFIG.y("postgresql.sslcert"),
sslkey=CONFIG.y("postgresql.sslkey"),
dbname=CONFIG.get("postgresql.name"),
user=CONFIG.get("postgresql.user"),
password=CONFIG.get("postgresql.password"),
host=CONFIG.get("postgresql.host"),
port=int(CONFIG.get("postgresql.port")),
sslmode=CONFIG.get("postgresql.sslmode"),
sslrootcert=CONFIG.get("postgresql.sslrootcert"),
sslcert=CONFIG.get("postgresql.sslcert"),
sslkey=CONFIG.get("postgresql.sslkey"),
)
cursor = conn.cursor()
cursor.execute("SELECT id FROM authentik_install_id LIMIT 1;")

View File

@ -10,6 +10,8 @@ from django.contrib.sessions.exceptions import SessionInterrupted
from django.contrib.sessions.middleware import SessionMiddleware as UpstreamSessionMiddleware
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.middleware.csrf import CSRF_SESSION_KEY
from django.middleware.csrf import CsrfViewMiddleware as UpstreamCsrfViewMiddleware
from django.utils.cache import patch_vary_headers
from django.utils.http import http_date
from jwt import PyJWTError, decode, encode
@ -131,6 +133,29 @@ class SessionMiddleware(UpstreamSessionMiddleware):
return response
class CsrfViewMiddleware(UpstreamCsrfViewMiddleware):
"""Dynamically set secure depending if the upstream connection is TLS or not"""
def _set_csrf_cookie(self, request: HttpRequest, response: HttpResponse):
if settings.CSRF_USE_SESSIONS:
if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]:
request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"]
else:
secure = SessionMiddleware.is_secure(request)
response.set_cookie(
settings.CSRF_COOKIE_NAME,
request.META["CSRF_COOKIE"],
max_age=settings.CSRF_COOKIE_AGE,
domain=settings.CSRF_COOKIE_DOMAIN,
path=settings.CSRF_COOKIE_PATH,
secure=secure,
httponly=settings.CSRF_COOKIE_HTTPONLY,
samesite=settings.CSRF_COOKIE_SAMESITE,
)
# Set the Vary header since content varies with the CSRF cookie.
patch_vary_headers(response, ("Cookie",))
class ChannelsLoggingMiddleware:
"""Logging middleware for channels"""

View File

@ -24,8 +24,8 @@ BASE_DIR = Path(__file__).absolute().parent.parent.parent
STATICFILES_DIRS = [BASE_DIR / Path("web")]
MEDIA_ROOT = BASE_DIR / Path("media")
DEBUG = CONFIG.y_bool("debug")
SECRET_KEY = CONFIG.y("secret_key")
DEBUG = CONFIG.get_bool("debug")
SECRET_KEY = CONFIG.get("secret_key")
INTERNAL_IPS = ["127.0.0.1"]
ALLOWED_HOSTS = ["*"]
@ -40,7 +40,7 @@ CSRF_COOKIE_NAME = "authentik_csrf"
CSRF_HEADER_NAME = "HTTP_X_AUTHENTIK_CSRF"
LANGUAGE_COOKIE_NAME = "authentik_language"
SESSION_COOKIE_NAME = "authentik_session"
SESSION_COOKIE_DOMAIN = CONFIG.y("cookie_domain", None)
SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None)
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
@ -66,7 +66,6 @@ INSTALLED_APPS = [
"authentik.crypto",
"authentik.events",
"authentik.flows",
"authentik.lib",
"authentik.outposts",
"authentik.policies.dummy",
"authentik.policies.event_matcher",
@ -146,6 +145,7 @@ SPECTACULAR_SETTINGS = {
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
"LDAPAPIAccessMode": "authentik.providers.ldap.models.APIAccessMode",
"UserVerificationEnum": "authentik.stages.authenticator_webauthn.models.UserVerification",
"UserTypeEnum": "authentik.core.models.UserTypes",
},
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,
"POSTPROCESSING_HOOKS": [
@ -178,26 +178,26 @@ REST_FRAMEWORK = {
"TEST_REQUEST_DEFAULT_FORMAT": "json",
"DEFAULT_THROTTLE_CLASSES": ["rest_framework.throttling.AnonRateThrottle"],
"DEFAULT_THROTTLE_RATES": {
"anon": CONFIG.y("throttle.default"),
"anon": CONFIG.get("throttle.default"),
},
}
_redis_protocol_prefix = "redis://"
_redis_celery_tls_requirements = ""
if CONFIG.y_bool("redis.tls", False):
if CONFIG.get_bool("redis.tls", False):
_redis_protocol_prefix = "rediss://"
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.y('redis.tls_reqs')}"
_redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
_redis_url = (
f"{_redis_protocol_prefix}:"
f"{quote_plus(CONFIG.y('redis.password'))}@{quote_plus(CONFIG.y('redis.host'))}:"
f"{int(CONFIG.y('redis.port'))}"
f"{quote_plus(CONFIG.get('redis.password'))}@{quote_plus(CONFIG.get('redis.host'))}:"
f"{int(CONFIG.get('redis.port'))}"
)
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"{_redis_url}/{CONFIG.y('redis.db')}",
"TIMEOUT": int(CONFIG.y("redis.cache_timeout", 300)),
"LOCATION": f"{_redis_url}/{CONFIG.get('redis.db')}",
"TIMEOUT": int(CONFIG.get("redis.cache_timeout", 300)),
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
"KEY_PREFIX": "authentik_cache",
}
@ -225,7 +225,7 @@ MIDDLEWARE = [
"authentik.events.middleware.AuditMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"authentik.root.middleware.CsrfViewMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"authentik.core.middleware.ImpersonateMiddleware",
@ -237,7 +237,7 @@ ROOT_URLCONF = "authentik.root.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [CONFIG.y("email.template_dir")],
"DIRS": [CONFIG.get("email.template_dir")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
@ -257,7 +257,7 @@ CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [f"{_redis_url}/{CONFIG.y('redis.db')}"],
"hosts": [f"{_redis_url}/{CONFIG.get('redis.db')}"],
"prefix": "authentik_channels",
},
},
@ -269,34 +269,37 @@ CHANNEL_LAYERS = {
DATABASES = {
"default": {
"ENGINE": "django_prometheus.db.backends.postgresql",
"HOST": CONFIG.y("postgresql.host"),
"NAME": CONFIG.y("postgresql.name"),
"USER": CONFIG.y("postgresql.user"),
"PASSWORD": CONFIG.y("postgresql.password"),
"PORT": int(CONFIG.y("postgresql.port")),
"SSLMODE": CONFIG.y("postgresql.sslmode"),
"SSLROOTCERT": CONFIG.y("postgresql.sslrootcert"),
"SSLCERT": CONFIG.y("postgresql.sslcert"),
"SSLKEY": CONFIG.y("postgresql.sslkey"),
"ENGINE": "authentik.root.db",
"HOST": CONFIG.get("postgresql.host"),
"NAME": CONFIG.get("postgresql.name"),
"USER": CONFIG.get("postgresql.user"),
"PASSWORD": CONFIG.get("postgresql.password"),
"PORT": int(CONFIG.get("postgresql.port")),
"SSLMODE": CONFIG.get("postgresql.sslmode"),
"SSLROOTCERT": CONFIG.get("postgresql.sslrootcert"),
"SSLCERT": CONFIG.get("postgresql.sslcert"),
"SSLKEY": CONFIG.get("postgresql.sslkey"),
}
}
if CONFIG.y_bool("postgresql.use_pgbouncer", False):
if CONFIG.get_bool("postgresql.use_pgbouncer", False):
# https://docs.djangoproject.com/en/4.0/ref/databases/#transaction-pooling-server-side-cursors
DATABASES["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
# https://docs.djangoproject.com/en/4.0/ref/databases/#persistent-connections
DATABASES["default"]["CONN_MAX_AGE"] = None # persistent
# Email
EMAIL_HOST = CONFIG.y("email.host")
EMAIL_PORT = int(CONFIG.y("email.port"))
EMAIL_HOST_USER = CONFIG.y("email.username")
EMAIL_HOST_PASSWORD = CONFIG.y("email.password")
EMAIL_USE_TLS = CONFIG.y_bool("email.use_tls", False)
EMAIL_USE_SSL = CONFIG.y_bool("email.use_ssl", False)
EMAIL_TIMEOUT = int(CONFIG.y("email.timeout"))
DEFAULT_FROM_EMAIL = CONFIG.y("email.from")
# These values should never actually be used, emails are only sent from email stages, which
# loads the config directly from CONFIG
# See authentik/stages/email/models.py, line 105
EMAIL_HOST = CONFIG.get("email.host")
EMAIL_PORT = int(CONFIG.get("email.port"))
EMAIL_HOST_USER = CONFIG.get("email.username")
EMAIL_HOST_PASSWORD = CONFIG.get("email.password")
EMAIL_USE_TLS = CONFIG.get_bool("email.use_tls", False)
EMAIL_USE_SSL = CONFIG.get_bool("email.use_ssl", False)
EMAIL_TIMEOUT = int(CONFIG.get("email.timeout"))
DEFAULT_FROM_EMAIL = CONFIG.get("email.from")
SERVER_EMAIL = DEFAULT_FROM_EMAIL
EMAIL_SUBJECT_PREFIX = "[authentik] "
@ -344,15 +347,15 @@ CELERY = {
},
"task_create_missing_queues": True,
"task_default_queue": "authentik",
"broker_url": f"{_redis_url}/{CONFIG.y('redis.db')}{_redis_celery_tls_requirements}",
"result_backend": f"{_redis_url}/{CONFIG.y('redis.db')}{_redis_celery_tls_requirements}",
"broker_url": f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
"result_backend": f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
}
# Sentry integration
env = get_env()
_ERROR_REPORTING = CONFIG.y_bool("error_reporting.enabled", False)
_ERROR_REPORTING = CONFIG.get_bool("error_reporting.enabled", False)
if _ERROR_REPORTING:
sentry_env = CONFIG.y("error_reporting.environment", "customer")
sentry_env = CONFIG.get("error_reporting.environment", "customer")
sentry_init()
set_tag("authentik.uuid", sha512(str(SECRET_KEY).encode("ascii")).hexdigest()[:16])
@ -366,7 +369,7 @@ MEDIA_URL = "/media/"
TEST = False
TEST_RUNNER = "authentik.root.test_runner.PytestTestRunner"
# We can't check TEST here as its set later by the test runner
LOG_LEVEL = CONFIG.y("log_level").upper() if "TF_BUILD" not in os.environ else "DEBUG"
LOG_LEVEL = CONFIG.get("log_level").upper() if "TF_BUILD" not in os.environ else "DEBUG"
# We could add a custom level to stdlib logging and structlog, but it's not easy or clean
# https://stackoverflow.com/questions/54505487/custom-log-level-not-working-with-structlog
# Additionally, the entire code uses debug as highest level so that would have to be re-written too

View File

@ -31,14 +31,14 @@ class PytestTestRunner: # pragma: no cover
settings.TEST = True
settings.CELERY["task_always_eager"] = True
CONFIG.y_set("avatars", "none")
CONFIG.y_set("geoip", "tests/GeoLite2-City-Test.mmdb")
CONFIG.y_set("blueprints_dir", "./blueprints")
CONFIG.y_set(
CONFIG.set("avatars", "none")
CONFIG.set("geoip", "tests/GeoLite2-City-Test.mmdb")
CONFIG.set("blueprints_dir", "./blueprints")
CONFIG.set(
"outposts.container_image_base",
f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}",
)
CONFIG.y_set("error_reporting.sample_rate", 0)
CONFIG.set("error_reporting.sample_rate", 0)
sentry_init(
environment="testing",
send_default_pii=True,

View File

@ -3,7 +3,10 @@ from django.core.management.base import BaseCommand
from structlog.stdlib import get_logger
from authentik.sources.ldap.models import LDAPSource
from authentik.sources.ldap.tasks import ldap_sync_single
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
from authentik.sources.ldap.tasks import ldap_sync_paginator
LOGGER = get_logger()
@ -20,4 +23,10 @@ class Command(BaseCommand):
if not source:
LOGGER.warning("Source does not exist", slug=source_slug)
continue
ldap_sync_single(source)
tasks = (
ldap_sync_paginator(source, UserLDAPSynchronizer)
+ ldap_sync_paginator(source, GroupLDAPSynchronizer)
+ ldap_sync_paginator(source, MembershipLDAPSynchronizer)
)
for task in tasks:
task()

View File

@ -136,7 +136,7 @@ class LDAPSource(Source):
chmod(private_key_file, 0o600)
tls_kwargs["local_private_key_file"] = private_key_file
tls_kwargs["local_certificate_file"] = certificate_file
if ciphers := CONFIG.y("ldap.tls.ciphers", None):
if ciphers := CONFIG.get("ldap.tls.ciphers", None):
tls_kwargs["ciphers"] = ciphers.strip()
if self.sni:
tls_kwargs["sni"] = self.server_uri.split(",", maxsplit=1)[0].strip()

View File

@ -93,7 +93,7 @@ class BaseLDAPSynchronizer:
types_only=False,
get_operational_attributes=False,
controls=None,
paged_size=int(CONFIG.y("ldap.page_size", 50)),
paged_size=int(CONFIG.get("ldap.page_size", 50)),
paged_criticality=False,
):
"""Search in pages, returns each page"""

View File

@ -49,7 +49,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
uniq = self._flatten(attributes[self._source.object_uniqueness_field])
try:
defaults = self.build_user_properties(user_dn, **attributes)
self._logger.debug("Creating user with attributes", **defaults)
self._logger.debug("Writing user with attributes", **defaults)
if "username" not in defaults:
raise IntegrityError("Username was not set by propertymappings")
ak_user, created = self.update_or_create_attributes(

View File

@ -20,6 +20,7 @@ class FreeIPA(BaseLDAPSynchronizer):
def sync(self, attributes: dict[str, Any], user: User, created: bool):
self.check_pwd_last_set(attributes, user, created)
self.check_nsaccountlock(attributes, user)
def check_pwd_last_set(self, attributes: dict[str, Any], user: User, created: bool):
"""Check krbLastPwdChange"""
@ -37,3 +38,14 @@ class FreeIPA(BaseLDAPSynchronizer):
)
user.set_unusable_password()
user.save()
def check_nsaccountlock(self, attributes: dict[str, Any], user: User):
"""https://www.port389.org/docs/389ds/howto/howto-account-inactivation.html"""
# This is more of a 389-ds quirk rather than FreeIPA, but FreeIPA uses
# 389-ds and this will trigger regardless
if "nsaccountlock" not in attributes:
return
is_active = attributes.get("nsaccountlock", False)
if is_active != user.is_active:
user.is_active = is_active
user.save()

View File

@ -78,5 +78,7 @@ class MicrosoftActiveDirectory(BaseLDAPSynchronizer):
# /useraccountcontrol-manipulate-account-properties
uac_bit = attributes.get("userAccountControl", 512)
uac = UserAccountControl(uac_bit)
user.is_active = UserAccountControl.ACCOUNTDISABLE not in uac
user.save()
is_active = UserAccountControl.ACCOUNTDISABLE not in uac
if is_active != user.is_active:
user.is_active = is_active
user.save()

View File

@ -59,7 +59,7 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
signatures = []
for page in sync_inst.get_objects():
page_cache_key = CACHE_KEY_PREFIX + str(uuid4())
cache.set(page_cache_key, page)
cache.set(page_cache_key, page, 60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")))
page_sync = ldap_sync.si(source.pk, class_to_path(sync), page_cache_key)
signatures.append(page_sync)
return signatures
@ -68,12 +68,12 @@ def ldap_sync_paginator(source: LDAPSource, sync: type[BaseLDAPSynchronizer]) ->
@CELERY_APP.task(
bind=True,
base=MonitoredTask,
soft_time_limit=60 * 60 * int(CONFIG.y("ldap.task_timeout_hours")),
task_time_limit=60 * 60 * int(CONFIG.y("ldap.task_timeout_hours")),
soft_time_limit=60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")),
task_time_limit=60 * 60 * int(CONFIG.get("ldap.task_timeout_hours")),
)
def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_key: str):
"""Synchronization of an LDAP Source"""
self.result_timeout_hours = int(CONFIG.y("ldap.task_timeout_hours"))
self.result_timeout_hours = int(CONFIG.get("ldap.task_timeout_hours"))
source: LDAPSource = LDAPSource.objects.filter(pk=source_pk).first()
if not source:
# Because the source couldn't be found, we don't have a UID
@ -86,6 +86,12 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str, page_cache_k
sync_inst: BaseLDAPSynchronizer = sync(source)
page = cache.get(page_cache_key)
if not page:
error_message = (
f"Could not find page in cache: {page_cache_key}. "
+ "Try increasing ldap.task_timeout_hours"
)
LOGGER.warning(error_message)
self.set_status(TaskResult(TaskResultStatus.ERROR, [error_message]))
return
cache.touch(page_cache_key)
count = sync_inst.sync(page)

View File

@ -8,12 +8,14 @@ from authentik.blueprints.tests import apply_blueprint
from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import Event, EventAction
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
from authentik.lib.generators import generate_key
from authentik.lib.utils.reflection import class_to_path
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
from authentik.sources.ldap.tasks import ldap_sync_all
from authentik.sources.ldap.tasks import ldap_sync, ldap_sync_all
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
@ -33,6 +35,14 @@ class LDAPSyncTests(TestCase):
additional_group_dn="ou=groups",
)
def test_sync_missing_page(self):
"""Test sync with missing page"""
connection = MagicMock(return_value=mock_ad_connection(LDAP_PASSWORD))
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
ldap_sync.delay(self.source.pk, class_to_path(UserLDAPSynchronizer), "foo").get()
status = TaskInfo.by_name("ldap_sync:ldap:users:foo")
self.assertEqual(status.result.status, TaskResultStatus.ERROR)
def test_sync_error(self):
"""Test user sync"""
self.source.property_mappings.set(

View File

@ -13,6 +13,7 @@ from rest_framework.serializers import BaseSerializer
from structlog.stdlib import get_logger
from authentik.flows.models import Stage
from authentik.lib.config import CONFIG
LOGGER = get_logger()
@ -104,7 +105,16 @@ class EmailStage(Stage):
def backend(self) -> BaseEmailBackend:
"""Get fully configured Email Backend instance"""
if self.use_global_settings:
return self.backend_class()
CONFIG.refresh("email.password")
return self.backend_class(
host=CONFIG.get("email.host"),
port=int(CONFIG.get("email.port")),
username=CONFIG.get("email.username"),
password=CONFIG.get("email.password"),
use_tls=CONFIG.get_bool("email.use_tls", False),
use_ssl=CONFIG.get_bool("email.use_ssl", False),
timeout=int(CONFIG.get("email.timeout")),
)
return self.backend_class(
host=self.host,
port=self.port,

View File

@ -13,6 +13,7 @@ from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_PLAN
from authentik.lib.config import CONFIG
from authentik.stages.email.models import EmailStage
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE
@ -120,7 +121,7 @@ class TestEmailStage(FlowTestCase):
def test_use_global_settings(self):
"""Test use_global_settings"""
host = "some-unique-string"
with self.settings(EMAIL_HOST=host):
with CONFIG.patch("email.host", host):
self.assertEqual(EmailStage(use_global_settings=True).backend.host, host)
def test_token(self):

View File

@ -179,7 +179,7 @@ class ListPolicyEngine(PolicyEngine):
self.__list = policies
self.use_cache = False
def _iter_bindings(self) -> Iterator[PolicyBinding]:
def iterate_bindings(self) -> Iterator[PolicyBinding]:
for policy in self.__list:
yield PolicyBinding(
policy=policy,

View File

@ -78,7 +78,7 @@ class CurrentTenantSerializer(PassiveSerializer):
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
default=CONFIG.y("footer_links", []),
default=CONFIG.get("footer_links", []),
)
ui_theme = ChoiceField(
choices=Themes.choices,

View File

@ -24,7 +24,7 @@ class TestTenants(APITestCase):
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"matched_domain": tenant.domain,
"ui_footer_links": CONFIG.y("footer_links"),
"ui_footer_links": CONFIG.get("footer_links"),
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
},
@ -43,7 +43,7 @@ class TestTenants(APITestCase):
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "custom",
"matched_domain": "bar.baz",
"ui_footer_links": CONFIG.y("footer_links"),
"ui_footer_links": CONFIG.get("footer_links"),
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
},
@ -59,7 +59,7 @@ class TestTenants(APITestCase):
"branding_favicon": "/static/dist/assets/icons/icon.png",
"branding_title": "authentik",
"matched_domain": "fallback",
"ui_footer_links": CONFIG.y("footer_links"),
"ui_footer_links": CONFIG.get("footer_links"),
"ui_theme": Themes.AUTOMATIC,
"default_locale": "",
},

View File

@ -36,7 +36,7 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
trace = span.to_traceparent()
return {
"tenant": tenant,
"footer_links": CONFIG.y("footer_links"),
"footer_links": CONFIG.get("footer_links"),
"sentry_trace": trace,
"version": get_full_version(),
}

View File

@ -94,21 +94,21 @@ entries:
prompt_data = request.context.get("prompt_data")
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_EMAIL, CONFIG.y_bool("default_user_change_email", True)
USER_ATTRIBUTE_CHANGE_EMAIL, CONFIG.get_bool("default_user_change_email", True)
):
if prompt_data.get("email") != request.user.email:
ak_message("Not allowed to change email address.")
return False
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_NAME, CONFIG.y_bool("default_user_change_name", True)
USER_ATTRIBUTE_CHANGE_NAME, CONFIG.get_bool("default_user_change_name", True)
):
if prompt_data.get("name") != request.user.name:
ak_message("Not allowed to change name.")
return False
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_USERNAME, CONFIG.y_bool("default_user_change_username", True)
USER_ATTRIBUTE_CHANGE_USERNAME, CONFIG.get_bool("default_user_change_username", True)
):
if prompt_data.get("username") != request.user.username:
ak_message("Not allowed to change username.")

View File

@ -3213,7 +3213,6 @@
"authentik.crypto",
"authentik.events",
"authentik.flows",
"authentik.lib",
"authentik.outposts",
"authentik.policies.dummy",
"authentik.policies.event_matcher",
@ -3980,6 +3979,16 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
@ -4171,6 +4180,16 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
@ -4366,6 +4385,16 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
@ -6522,6 +6551,16 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
@ -7257,6 +7296,16 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
}
},
"required": [
@ -8334,6 +8383,16 @@
"minLength": 1,
"title": "Path"
},
"type": {
"type": "string",
"enum": [
"internal",
"external",
"service_account",
"internal_service_account"
],
"title": "Type"
},
"password": {
"type": "string",
"minLength": 1,

View File

@ -1,36 +0,0 @@
# This file is used for development and debugging, and should not be used for production instances
version: '3.5'
services:
flower:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.5.4}
restart: unless-stopped
command: worker-status
environment:
AUTHENTIK_REDIS__HOST: redis
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
env_file:
- .env
ports:
- "9001:9000"
depends_on:
- postgresql
- redis
server:
environment:
AUTHENTIK_REMOTE_DEBUG: "true"
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
ports:
- 6800:6800
worker:
environment:
CELERY_RDB_HOST: "0.0.0.0"
CELERY_RDBSIG: "1"
AUTHENTIK_REMOTE_DEBUG: "true"
PYDEVD_THREAD_DUMP_ON_WARN_EVALUATION_TIMEOUT: "true"
ports:
- 6900:6900

2
go.mod
View File

@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
goauthentik.io/api/v3 v3.2023054.4
goauthentik.io/api/v3 v3.2023061.6
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0

4
go.sum
View File

@ -1070,8 +1070,8 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
goauthentik.io/api/v3 v3.2023054.4 h1:wnONALlxADR42TpW5xKKsGkJ/G8oNDQsWiwdlMsG2Ig=
goauthentik.io/api/v3 v3.2023054.4/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
goauthentik.io/api/v3 v3.2023061.6 h1:4zbo0Dtx42HLYObizIlTWAk7iBvCv9kmCvzBxMElkIk=
goauthentik.io/api/v3 v3.2023061.6/go.mod h1:tC7qK9VSP0zJah5p5xHFnjZt/4dAkXVwcrWyZNGYhwQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=

View File

@ -8,6 +8,7 @@ import (
"net/http/cookiejar"
"net/url"
"strings"
"time"
"github.com/getsentry/sentry-go"
"github.com/prometheus/client_golang/prometheus"
@ -21,10 +22,20 @@ import (
var (
FlowTimingGet = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_get_seconds",
Help: "Duration it took to get a challenge in seconds",
}, []string{"stage", "flow"})
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_post_seconds",
Help: "Duration it took to send a challenge in seconds",
}, []string{"stage", "flow"})
// NOTE: the following metrics are kept for compatibility purpose
FlowTimingGetLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_get",
Help: "Duration it took to get a challenge",
}, []string{"stage", "flow"})
FlowTimingPost = promauto.NewHistogramVec(prometheus.HistogramOpts{
FlowTimingPostLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_flow_timing_post",
Help: "Duration it took to send a challenge",
}, []string{"stage", "flow"})
@ -186,6 +197,10 @@ func (fe *FlowExecutor) getInitialChallenge() (*api.ChallengeTypes, error) {
FlowTimingGet.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)) / float64(time.Second))
FlowTimingGetLegacy.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(gcsp.EndTime.Sub(gcsp.StartTime)))
return challenge, nil
}
@ -243,6 +258,10 @@ func (fe *FlowExecutor) solveFlowChallenge(challenge *api.ChallengeTypes, depth
FlowTimingPost.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)) / float64(time.Second))
FlowTimingPostLegacy.With(prometheus.Labels{
"stage": ch.GetComponent(),
"flow": fe.flowSlug,
}).Observe(float64(scsp.EndTime.Sub(scsp.StartTime)))
if depth >= 10 {

View File

@ -2,6 +2,7 @@ package ldap
import (
"net"
"time"
"beryju.io/ldap"
"github.com/getsentry/sentry-go"
@ -20,6 +21,11 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Bind request")
}()
@ -49,6 +55,12 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
"reason": "no_provider",
"app": "",
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "bind",
"reason": "no_provider",
"app": "",
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}

View File

@ -52,6 +52,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "flow_error",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to execute flow")
return ldap.LDAPResultInvalidCredentials, nil
}
@ -62,6 +68,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "invalid_credentials",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().Info("Invalid credentials")
return ldap.LDAPResultInvalidCredentials, nil
}
@ -75,6 +87,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_denied",
"app": db.si.GetAppSlug(),
}).Inc()
return ldap.LDAPResultInsufficientAccessRights, nil
}
if err != nil {
@ -84,6 +102,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "access_check_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to check access")
return ldap.LDAPResultOperationsError, nil
}
@ -98,6 +122,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": db.si.GetOutpostName(),
"type": "bind",
"reason": "user_info_fail",
"app": db.si.GetAppSlug(),
}).Inc()
req.Log().WithError(err).Warning("failed to get user info")
return ldap.LDAPResultOperationsError, nil
}

View File

@ -15,10 +15,20 @@ import (
var (
Requests = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_ldap_request_duration_seconds",
Help: "LDAP request latencies in seconds",
}, []string{"outpost_name", "type", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_ldap_requests_rejected_total",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "type", "reason", "app"})
// NOTE: the following metrics are kept for compatibility purpose
RequestsLegacy = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "authentik_outpost_ldap_requests",
Help: "The total number of configured providers",
}, []string{"outpost_name", "type", "app"})
RequestsRejected = promauto.NewCounterVec(prometheus.CounterOpts{
RequestsRejectedLegacy = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "authentik_outpost_ldap_requests_rejected",
Help: "Total number of rejected requests",
}, []string{"outpost_name", "type", "reason", "app"})

View File

@ -2,6 +2,7 @@ package ldap
import (
"net"
"time"
"beryju.io/ldap"
"github.com/getsentry/sentry-go"
@ -21,6 +22,11 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
"outpost_name": ls.ac.Outpost.Name,
"type": "search",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)) / float64(time.Second))
metrics.RequestsLegacy.With(prometheus.Labels{
"outpost_name": ls.ac.Outpost.Name,
"type": "search",
"app": selectedApp,
}).Observe(float64(span.EndTime.Sub(span.StartTime)))
req.Log().WithField("attributes", searchReq.Attributes).WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Search request")
}()

View File

@ -45,6 +45,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "empty_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "empty_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
}
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
@ -54,6 +60,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "invalid_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "invalid_bind_dn",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: BindDN %s not in our BaseDN %s", req.BindDN, ds.si.GetBaseDN())
}
@ -66,6 +78,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "user_info_not_cached",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "user_info_not_cached",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}
accsp.Finish()
@ -78,6 +96,12 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
"reason": "filter_parse_fail",
"app": ds.si.GetAppSlug(),
}).Inc()
metrics.RequestsRejectedLegacy.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
"reason": "filter_parse_fail",
"app": ds.si.GetAppSlug(),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter)
}

View File

@ -41,7 +41,7 @@ func (ds *DirectSearcher) SearchSubschema(req *search.Request) (ldap.ServerSearc
// Custom attributes
// Temporarily use 1.3.6.1.4.1.26027.1.1 as a base
// https://docs.oracle.com/cd/E19450-01/820-6169/working-with-object-identifiers.html#obtaining-a-base-oid
"( 1.3.6.1.4.1.26027.1.1.1 NAME 'goauthentik.io/ldap/user' SUP organizationalPerson STRUCTURAL MAY ( ak-active $ sAMAccountName $ goauthentikio-user-sources $ goauthentik.io/user/sources $ goauthentik.io/ldap/active $ goauthentik.io/ldap/superuser $ goauthentikio-user-override-ips $ goauthentikio-user-service-account ) )",
"( 1.3.6.1.4.1.26027.1.1.1 NAME 'goauthentik.io/ldap/user' SUP organizationalPerson STRUCTURAL MAY ( ak-active $ sAMAccountName $ goauthentikio-user-sources $ goauthentik.io/user/sources $ goauthentik.io/ldap/active $ goauthentik.io/ldap/superuser ) )",
},
},
{
@ -85,8 +85,6 @@ func (ds *DirectSearcher) SearchSubschema(req *search.Request) (ldap.ServerSearc
// https://docs.oracle.com/cd/E19450-01/820-6169/working-with-object-identifiers.html#obtaining-a-base-oid
"( 1.3.6.1.4.1.26027.1.1.2 NAME ( 'goauthentik.io/ldap/superuser' 'ak-superuser' ) SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )",
"( 1.3.6.1.4.1.26027.1.1.3 NAME ( 'goauthentik.io/ldap/active' 'ak-active' ) SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )",
"( 1.3.6.1.4.1.26027.1.1.4 NAME 'goauthentikio-user-override-ips' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )",
"( 1.3.6.1.4.1.26027.1.1.5 NAME 'goauthentikio-user-service-account' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE' )",
},
},
},

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