Compare commits

..

201 Commits

Author SHA1 Message Date
10b1bd5bf4 release: 2022.9.1 2022-11-02 21:54:21 +01:00
55cc1812c0 core: bump golang from 1.19.2-bullseye to 1.19.3-bullseye (#3925)
Bumps golang from 1.19.2-bullseye to 1.19.3-bullseye.

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-02 10:09:00 +01:00
6962d8c59c core: bump golang from 1.19.1-bullseye to 1.19.2-bullseye (#3728) 2022-11-02 10:08:54 +01:00
2cfba36cb7 release: 2022.9.0 2022-09-23 12:33:01 +02:00
73bfe19f0e web: bump @codemirror/lang-python from 6.0.1 to 6.0.2 in /web (#3631) 2022-09-23 09:21:52 +02:00
64e211c3a9 web: bump rollup from 2.79.0 to 2.79.1 in /web (#3632) 2022-09-23 09:21:35 +02:00
3c354db858 core: bump djangorestframework from 3.13.1 to 3.14.0 (#3633) 2022-09-23 09:21:25 +02:00
99df72d944 core: bump drf-spectacular from 0.24.0 to 0.24.1 (#3634) 2022-09-23 09:20:58 +02:00
374b10b6e5 root: update bumpversion config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-22 23:44:49 +02:00
4c606fb0ba web/admin: more diagrams (#3630)
* separate diagram element

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

* add more

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-22 21:27:29 +02:00
f8502edd2b website: update 2022.9 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-22 10:46:29 +02:00
aa48f6dd9d web/flows: fix ak-locale prompt being rendered without name attribute
closes #3100

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-22 10:35:20 +02:00
49b6aabb02 outposts/proxy: fix redirect path when external host is a subdirectory (#3628)
fix redirect path when external host is a subdirectory

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-22 10:10:29 +02:00
f9d9b2716d core: bump twilio from 7.14.0 to 7.14.1 (#3629) 2022-09-22 09:48:14 +02:00
7932b390dc web: cleanup old FlowDiagram colours
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-21 20:23:20 +02:00
241e36b2a6 web: fix scrolling in modals in low-height views (#3596) 2022-09-21 19:37:57 +02:00
81e820b6e6 flows: fix invalid graph generation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-21 10:53:29 +02:00
b16a3d5697 internal: use config system for workers/threads, document the settings (#3626)
use config system for workers/threads, document the settings

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-21 09:59:03 +02:00
1583d53e54 web: use mermaidjs (#3623)
* flows: move flow diagram logic to separate file

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

* idk

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

* make web component work

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

* remove subgraph for now

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

* cleanup

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

* add denied connection

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

* fix

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

* wrong list

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

* fix tests

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

* use custom styles

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

* i18n

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

* fix typing issues, make diagram centered

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

* fix tests

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

* fix lint

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-21 09:58:23 +02:00
909a7772dc web: bump pyright from 1.1.271 to 1.1.272 in /web (#3624) 2022-09-21 08:57:41 +02:00
cd42b013ca web: bump @codemirror/lang-javascript from 6.0.2 to 6.1.0 in /web (#3625) 2022-09-21 08:57:31 +02:00
22a2e65d30 web: manually update translations
closes #3296 closes #3455

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-20 09:59:25 +02:00
f3fe2c1b4a web: bump @typescript-eslint/eslint-plugin from 5.37.0 to 5.38.0 in /web (#3617) 2022-09-20 09:17:59 +02:00
133accd033 web: bump @typescript-eslint/parser from 5.37.0 to 5.38.0 in /web (#3618) 2022-09-20 09:16:33 +02:00
47daaf969a outposts: fix oauth state when using signature routing (#3616)
* fix oauth state when using signature routing

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

* more retires

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-19 21:38:34 +02:00
9fb5092fdc root: shorten outpost healthcheck intervals
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-19 20:31:34 +02:00
05ccff4651 web: fix checkbox styling on applications form
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-19 17:38:04 +02:00
2d965afc5f website: bump react-before-after-slider-component from 1.1.4 to 1.1.5 in /website (#3612)
website: bump react-before-after-slider-component in /website

Bumps [react-before-after-slider-component](https://github.com/smeleshkin/react-before-after-slider-component) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/smeleshkin/react-before-after-slider-component/releases)
- [Commits](https://github.com/smeleshkin/react-before-after-slider-component/compare/v.1.1.4...v.1.1.5)

---
updated-dependencies:
- dependency-name: react-before-after-slider-component
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 10:45:10 +02:00
522abfd2fd web: bump @patternfly/patternfly from 4.210.2 to 4.215.1 in /web (#3613)
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.210.2 to 4.215.1.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/main/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/prerelease-v4.210.2...prerelease-v4.215.1)

---
updated-dependencies:
- dependency-name: "@patternfly/patternfly"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 10:44:49 +02:00
2d1bcf1aa7 core: bump goauthentik.io/api/v3 from 3.2022082.5 to 3.2022082.6 (#3615)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022082.5 to 3.2022082.6.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022082.5...v3.2022082.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 10:44:37 +02:00
24dbfcea3f core: bump pyjwt from 2.4.0 to 2.5.0 (#3614)
Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.4.0 to 2.5.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.4.0...2.5.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-19 10:37:34 +02:00
f008c03524 website: cleanup readme
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-18 18:45:05 +02:00
9ab066a863 website: Fix padding in feature comparison table (#3611)
* Fix table block styling

* Add correct command to run development server

* Update website/src/css/custom.css

closes #3315

Co-authored-by: Jens L. <jens@beryju.org>
Signed-off-by: Issy Szemeti <48881813+issy@users.noreply.github.com>

Signed-off-by: Issy Szemeti <48881813+issy@users.noreply.github.com>
Co-authored-by: Jens L. <jens@beryju.org>
2022-09-18 16:43:05 +00:00
daa0417c38 website: fix broken link
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-18 17:57:19 +02:00
067166d420 website: update 2022.9 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-17 14:28:04 +02:00
2bd10dbdee tests: use create_test_flow where possible (#3606)
* use create_test_flow where possible

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

* fix and add more tests

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

* remove unused websocket stuff

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

* Revert "remove unused websocket stuff"

This reverts commit fc05f80951.

* keepdb for make test

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

* fix more

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

* add tests for notification transports

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-17 13:16:53 +02:00
09795fa6fb web: bump API Client version (#3607)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-09-17 12:13:33 +02:00
be64296494 stages/authenticator_duo: improved import (#3601)
* prepare for duo admin integration

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

* make duo import params required

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

* add UI to import devices

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

* rework form, automatic import

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

* limit amount of concurrent tasks on worker

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

* load tasks

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

* fix API codes

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

* fix tests and such

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

* add tests

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

* sigh

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

* make stage better

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

* basic stage test

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-17 12:10:47 +02:00
02e2c117ac web: Fixed typo in stages/user_write form (#3604) 2022-09-17 11:51:15 +02:00
75434cc23f core: bump oauthlib from 3.2.0 to 3.2.1 (#3603)
Bumps [oauthlib](https://github.com/oauthlib/oauthlib) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/oauthlib/oauthlib/releases)
- [Changelog](https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/oauthlib/oauthlib/compare/v3.2.0...v3.2.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-17 00:26:39 +02:00
778e316690 web: update locales after web move
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-16 17:57:50 +02:00
3e0778fe31 website: add API diff to 2022.9 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-16 10:20:26 +02:00
d2390eef89 web: bump @sentry/browser from 7.12.1 to 7.13.0 in /web (#3598) 2022-09-16 09:30:17 +02:00
5c542d5dc2 web: bump @sentry/tracing from 7.12.1 to 7.13.0 in /web (#3599) 2022-09-16 09:28:47 +02:00
6b5b72ab4c ci: use codecov flags
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-15 21:15:25 +02:00
93ae3c19b0 web: fix duplicate class name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-15 17:12:33 +02:00
a2ccdaca05 website/integrations: Add Gitea Helm Chart Configuration (#3558)
* website/integrations: Add Gitea Helm Chart Configuration

* website/integrations: Add Gitea Helm Chart Configuration - pr fixes
2022-09-15 10:24:17 +02:00
4a0e051c0b web: bump @babel/preset-env from 7.19.0 to 7.19.1 in /web (#3593)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.19.0 to 7.19.1.
- [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.19.1/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-15 10:00:53 +02:00
7ad2992fe7 core: bump drf-spectacular from 0.23.1 to 0.24.0 (#3595) 2022-09-15 09:33:41 +02:00
223000804e web: bump @babel/plugin-transform-runtime from 7.18.10 to 7.19.1 in /web (#3591) 2022-09-15 09:33:27 +02:00
7b0cb38c4b web: bump @babel/core from 7.19.0 to 7.19.1 in /web (#3592) 2022-09-15 09:33:21 +02:00
94d8465e92 web: bump @babel/plugin-proposal-decorators from 7.19.0 to 7.19.1 in /web (#3594) 2022-09-15 09:31:45 +02:00
4a91a7d2e2 web: re-organise frontend and cleanup common code (#3572)
* fix repo in api client

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

* web: re-organise files to match their interface

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

* core: include version in script tags

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

* cleanup maybe broken

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

* revert rename

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

* web: get rid of Client.ts

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

* move more to common

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

* more moving

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

* format

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

* unfuck files that vscode fucked, thanks

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

* move more

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

* finish moving (maybe)

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

* ok more moving

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

* fix more stuff that vs code destroyed

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

* get rid "web" prefix for virtual package

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

* fix locales

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

* use custom base element

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

* fix css file

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

* don't run autoDetectLanguage when importing locale

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

* fix circular dependencies

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

* web: fix build

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-15 00:05:21 +02:00
369440652c web: set default value for cert select inputs (#3577)
* web: set default value for cert select inputs

* web: remove quotes from cert option values

* check for undefined too

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 23:40:49 +02:00
493cdd5c0f blueprints: fix example blueprints not explicitly setting placeholder_expression
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 21:54:24 +02:00
9f5c019daa core: add helper function to create events from expressions, move ak_user_has_authenticator to base evaluator
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 21:52:41 +02:00
ab28370f20 web: turn off no-unknown-tag-name for now
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 21:33:28 +02:00
84c08dca41 stages/user_write: log discarded keys as warning
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 20:21:37 +02:00
6b8b596c92 stages/identification: set primary_action based on flow designation
closes #3589

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-14 10:30:49 +02:00
dc622a836f web: bump pyright from 1.1.270 to 1.1.271 in /web (#3590) 2022-09-14 09:14:48 +02:00
7b4faa0170 web: bump @typescript-eslint/eslint-plugin from 5.36.2 to 5.37.0 in /web (#3587)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.36.2 to 5.37.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/v5.37.0/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-13 10:13:22 +02:00
65f5f21de2 web: bump eslint from 8.23.0 to 8.23.1 in /web (#3585)
Bumps [eslint](https://github.com/eslint/eslint) from 8.23.0 to 8.23.1.
- [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.23.0...v8.23.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-13 09:48:25 +02:00
d7e2b2e8a0 web: bump @rollup/plugin-node-resolve from 14.0.1 to 14.1.0 in /web (#3588)
Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve) from 14.0.1 to 14.1.0.
- [Release notes](https://github.com/rollup/plugins/releases)
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/node-resolve/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/node-resolve-v14.1.0/packages/node-resolve)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-node-resolve"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-13 09:48:07 +02:00
c12c3877f6 web: bump @typescript-eslint/parser from 5.36.2 to 5.37.0 in /web (#3586)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.36.2 to 5.37.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.37.0/packages/parser)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-13 09:47:48 +02:00
9cc1b1213f web: bump @goauthentik/api from 2022.8.2-1662485118 to 2022.8.2-1662930968 in /web (#3578)
web: bump @goauthentik/api in /web

Bumps [@goauthentik/api](https://github.com/GIT_USER_ID/GIT_REPO_ID) from 2022.8.2-1662485118 to 2022.8.2-1662930968.
- [Release notes](https://github.com/GIT_USER_ID/GIT_REPO_ID/releases)
- [Commits](https://github.com/GIT_USER_ID/GIT_REPO_ID/commits)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 10:11:51 +02:00
d0f5e77f77 core: bump goauthentik.io/api/v3 from 3.2022082.3 to 3.2022082.5 (#3579)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022082.3 to 3.2022082.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022082.3...v3.2022082.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-12 10:11:41 +02:00
68950ee8b7 web: bump API Client version
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:20:27 +02:00
53f224300b internal: set ETag header on static resources to reduce cache issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#3456
2022-09-11 23:18:34 +02:00
73019c0732 web: bump API Client version (#3574)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-09-11 23:14:09 +02:00
359da6db81 Revert "flows: always mark component field as required in Challenge and ChallengeResponses"
This reverts commit b35b225453.
2022-09-11 23:13:51 +02:00
34928572db website/docs: fix lint
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:11:41 +02:00
7f8afad528 *: fix API Schema generation warnings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:08:31 +02:00
c1ad1e5c8b website: prepare 2022.9 release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:02:06 +02:00
b35b225453 flows: always mark component field as required in Challenge and ChallengeResponses
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:01:59 +02:00
0ff2ac7dc2 api: fix schema not referencing errors correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 23:01:26 +02:00
8b4a7666f0 stages/authenticator_duo: fix 404 when current user does not have permissions to view stage
closes #3288

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-11 21:43:29 +02:00
e477615b0f website/integrations: update harbor screenshot
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#3543
2022-09-11 16:25:17 +02:00
e99b90912f website: cleanup integrations sidebar
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 22:00:34 +02:00
ae9dbf3014 blueprints: fix error caused by overriding rest_framework's instance attribute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 14:34:43 +02:00
7a50d5a4f8 website: add note for using request.user in policies when bound to flows
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 14:08:37 +02:00
4c4d87d3bd blueprints: validate instance before creating in metaapplyblueprint
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:58:54 +02:00
a407334d3b providers/oauth2: use @method_decorator instead of decorating in urls
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:26:17 +02:00
5026cebf02 stages/consent: default to expiring consent instead of always_require
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:25:28 +02:00
9770ba07c2 ci: remove duplicate helper script
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:25:01 +02:00
2e2ab55f9e *: cleanup stray print calls
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:24:53 +02:00
28835fbca7 root: re-use custom log helper from config and cleanup duplicate functions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 13:24:31 +02:00
aabb8af486 tenants: handle all errors in default_locale
closes #3457

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-10 00:56:12 +02:00
371f1878a8 core: bump python from 3.10.6-slim-bullseye to 3.10.7-slim-bullseye (#3568) 2022-09-09 09:35:32 +02:00
662bd8af96 web: bump typescript from 4.8.2 to 4.8.3 in /web (#3569) 2022-09-09 09:35:21 +02:00
1b57cc3bf0 web: bump @rollup/plugin-node-resolve from 14.0.0 to 14.0.1 in /web (#3570) 2022-09-09 09:35:06 +02:00
7517d612d0 providers/oauth2: add x5c (#3556)
* add x5c, x5t and x5t#S256

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

* strip trailing = to fix encoding issues

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-08 23:30:05 +02:00
a29fabac42 core: bump twisted from 22.4.0 to 22.8.0 (#3560)
Bumps [twisted](https://github.com/twisted/twisted) from 22.4.0 to 22.8.0.
- [Release notes](https://github.com/twisted/twisted/releases)
- [Changelog](https://github.com/twisted/twisted/blob/trunk/NEWS.rst)
- [Commits](https://github.com/twisted/twisted/compare/twisted-22.4.0...twisted-22.8.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-08 10:01:07 +02:00
a0ff4ac038 core: bump twilio from 7.13.0 to 7.14.0 (#3561)
Bumps [twilio](https://github.com/twilio/twilio-python) from 7.13.0 to 7.14.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/7.13.0...7.14.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-08 10:00:58 +02:00
4f6e3516b9 core: bump pylint from 2.15.0 to 2.15.2 (#3562)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.15.0 to 2.15.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.15.0...v2.15.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-08 10:00:50 +02:00
220f123b29 internal: add more tracing for states
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-07 09:53:10 +02:00
3e70b6443a web: bump @rollup/plugin-node-resolve from 13.3.0 to 14.0.0 in /web (#3551)
Bumps [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/HEAD/packages/node-resolve) from 13.3.0 to 14.0.0.
- [Release notes](https://github.com/rollup/plugins/releases)
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/node-resolve/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/commonjs-v14.0.0/packages/node-resolve)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-node-resolve"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 09:39:56 +02:00
792531a968 core: bump golang from 1.19.0-bullseye to 1.19.1-bullseye (#3549)
Bumps golang from 1.19.0-bullseye to 1.19.1-bullseye.

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 09:39:48 +02:00
de17fdb3ff web: bump pyright from 1.1.269 to 1.1.270 in /web (#3550)
Bumps [pyright](https://github.com/Microsoft/pyright/tree/HEAD/packages/pyright) from 1.1.269 to 1.1.270.
- [Release notes](https://github.com/Microsoft/pyright/releases)
- [Commits](https://github.com/Microsoft/pyright/commits/1.1.270/packages/pyright)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 09:39:31 +02:00
19fdcba308 core: bump goauthentik.io/api/v3 from 3.2022082.2 to 3.2022082.3 (#3552)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022082.2 to 3.2022082.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022082.2...v3.2022082.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 09:39:10 +02:00
62f93c83d4 ci: update pyright (#3546) 2022-09-07 00:23:25 +02:00
03a3f1bd6f crypto: add command to import certificates
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#3544
2022-09-06 19:39:10 +02:00
c2cb804ace web: bump API Client version (#3545)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-09-06 19:33:34 +02:00
11334cf638 web: re-cleanup imports not being absolute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-06 19:02:40 +02:00
60266b3345 flows: migrate FlowExecutor error handler to native challenge instead of shell
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-06 18:48:15 +02:00
2a4679e390 flows: fix incorrect diagram for policies bound to flows
closes #3534

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-06 10:24:13 +02:00
34e71351a6 web: bump @typescript-eslint/eslint-plugin from 5.36.1 to 5.36.2 in /web (#3538)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.36.1 to 5.36.2.
- [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/v5.36.2/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:47:48 +02:00
f13af32102 web: bump @babel/plugin-proposal-decorators from 7.18.10 to 7.19.0 in /web (#3536)
web: bump @babel/plugin-proposal-decorators in /web

Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.18.10 to 7.19.0.
- [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.19.0/packages/babel-plugin-proposal-decorators)

---
updated-dependencies:
- dependency-name: "@babel/plugin-proposal-decorators"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:47:13 +02:00
832b5f1058 web: bump @rollup/plugin-typescript from 8.4.0 to 8.5.0 in /web (#3537)
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 8.4.0 to 8.5.0.
- [Release notes](https://github.com/rollup/plugins/releases)
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v8.5.0/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:47:01 +02:00
df789265f9 web: bump @typescript-eslint/parser from 5.36.1 to 5.36.2 in /web (#3539)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.36.1 to 5.36.2.
- [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.36.2/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:46:46 +02:00
b45fbbf20f web: bump @babel/core from 7.18.13 to 7.19.0 in /web (#3535)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.13 to 7.19.0.
- [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.19.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:46:38 +02:00
b4c500ce15 web: bump @babel/preset-env from 7.18.10 to 7.19.0 in /web (#3540)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.18.10 to 7.19.0.
- [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.19.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:46:04 +02:00
114226dc22 core: bump goauthentik.io/api/v3 from 3.2022082.1 to 3.2022082.2 (#3541)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022082.1 to 3.2022082.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022082.1...v3.2022082.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 09:45:45 +02:00
897446e5ac web: bump API Client version (#3533)
Signed-off-by: GitHub <noreply@github.com>

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-09-05 22:23:59 +02:00
eed958b132 stages/authenticator_duo: fix schema not declaring request body correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-05 22:00:02 +02:00
d0a69557d4 website/docs: explain LISTEN envs better (#3532)
From a recent adventure discovered that this env's define `address:port` not just `port`.
If you define only `port` it will error out with `"error":"listen tcp: address 9000: missing port in address"`

Signed-off-by: Stavros Kois <47820033+stavros-k@users.noreply.github.com>

Signed-off-by: Stavros Kois <47820033+stavros-k@users.noreply.github.com>
2022-09-05 20:37:11 +02:00
712d2b2aee web: bump @sentry/tracing from 7.12.0 to 7.12.1 in /web (#3524) 2022-09-05 18:47:09 +02:00
2293cecd24 website: bump @docusaurus/preset-classic from 2.0.1 to 2.1.0 in /website (#3525) 2022-09-05 18:46:59 +02:00
4e8f0bfb66 core: bump swagger-spec-validator from 2.7.5 to 2.7.6 (#3528)
Bumps [swagger-spec-validator](https://github.com/Yelp/swagger_spec_validator) from 2.7.5 to 2.7.6.
- [Release notes](https://github.com/Yelp/swagger_spec_validator/releases)
- [Changelog](https://github.com/Yelp/swagger_spec_validator/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Yelp/swagger_spec_validator/compare/v2.7.5...v2.7.6)

---
updated-dependencies:
- dependency-name: swagger-spec-validator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:17:10 +02:00
578139811c web: bump @sentry/browser from 7.12.0 to 7.12.1 in /web (#3523)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.12.0 to 7.12.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.12.0...7.12.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:15:06 +02:00
f806dea0f6 core: bump sentry-sdk from 1.9.7 to 1.9.8 (#3529)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.9.7 to 1.9.8.
- [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.9.7...1.9.8)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:14:51 +02:00
02ec42738c core: bump pytest from 7.1.2 to 7.1.3 (#3531)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.1.2...7.1.3)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:12:40 +02:00
86468163f0 core: bump django from 4.1 to 4.1.1 (#3530)
Bumps [django](https://github.com/django/django) from 4.1 to 4.1.1.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/commits)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:12:27 +02:00
159e533e4a website: bump @docusaurus/plugin-client-redirects from 2.0.1 to 2.1.0 in /website (#3526)
website: bump @docusaurus/plugin-client-redirects in /website

Bumps [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.1.0/packages/docusaurus-plugin-client-redirects)

---
updated-dependencies:
- dependency-name: "@docusaurus/plugin-client-redirects"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:12:03 +02:00
24ee2c6c05 web: bump @codemirror/lang-html from 6.1.0 to 6.1.1 in /web (#3527)
Bumps [@codemirror/lang-html](https://github.com/codemirror/lang-html) from 6.1.0 to 6.1.1.
- [Release notes](https://github.com/codemirror/lang-html/releases)
- [Changelog](https://github.com/codemirror/lang-html/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/lang-html/compare/6.1.0...6.1.1)

---
updated-dependencies:
- dependency-name: "@codemirror/lang-html"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-05 16:10:41 +02:00
dd383d763f Revert "tests: remove duplicate healthchecks, bump grafana and dex"
This reverts commit df9d8e9d25.
2022-09-04 17:58:30 +02:00
df9d8e9d25 tests: remove duplicate healthchecks, bump grafana and dex
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-04 17:15:21 +02:00
12c318f0b1 sources/ldap: start_tls before binding but without reading server info
with read_server_info=True (default), this errors out on active directory

closes #3509 #1049

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-04 14:04:08 +02:00
fc6ed8e7f9 root: make redis persistent in docker-compose
fixes compose repeatedly creating new volumes on updates

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-04 00:30:47 +02:00
f68ed3562e core: fix custom favicon not being set correctly on load
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-04 00:24:51 +02:00
621245aece internal: optimise outpost's flow executor to use less requests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-03 21:29:58 +02:00
f2f22719f8 core: improve error template (#3521) 2022-09-03 19:46:37 +02:00
242423cf3c internal: remove sentryhttp from main server mux to prevent double traces
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-03 16:41:47 +02:00
8e7a456f74 providers/proxy: fix routing based on signature in traefik and caddy
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-02 22:03:08 +02:00
3987f8e371 web/flows: update flow background
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-02 11:03:28 +02:00
6fb531c482 web: bump @patternfly/patternfly from 4.206.3 to 4.210.2 in /web (#3518)
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.206.3 to 4.210.2.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/main/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/prerelease-v4.206.3...prerelease-v4.210.2)

---
updated-dependencies:
- dependency-name: "@patternfly/patternfly"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 10:32:13 +02:00
159798a7d8 core: bump sentry-sdk from 1.9.6 to 1.9.7 (#3519)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.9.6 to 1.9.7.
- [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.9.6...1.9.7)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-02 10:32:02 +02:00
89f8962a23 website/integrations: improve 'Portainer' section (#3508)
* Cleared up confusion regarding portainer

Adding in a / at the end of the redirect url is crucial and failing to do so will cause a 'Redirect URL' error thrown in by authentik.
I also find it more clear to use 'portainer.company' instead of 'port.company'.


Signed-off-by: Matthieu B <66959271+mtthidoteu@users.noreply.github.com>

* fix lint

Signed-off-by: Matthieu B <66959271+mtthidoteu@users.noreply.github.com>
Co-authored-by: Jens L <jens@beryju.org>
2022-09-01 12:49:09 +02:00
3db0a5b3d1 web: bump @sentry/tracing from 7.11.1 to 7.12.0 in /web (#3513)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 7.11.1 to 7.12.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.11.1...7.12.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:03:58 +02:00
feb20c371c web: bump @sentry/browser from 7.11.1 to 7.12.0 in /web (#3512)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 7.11.1 to 7.12.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.11.1...7.12.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:02:42 +02:00
73081e4947 web: bump rollup from 2.78.1 to 2.79.0 in /web (#3514)
Bumps [rollup](https://github.com/rollup/rollup) from 2.78.1 to 2.79.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.78.1...v2.79.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:02:19 +02:00
d7c2d0c7f9 core: bump swagger-spec-validator from 2.7.4 to 2.7.5 (#3515)
Bumps [swagger-spec-validator](https://github.com/Yelp/swagger_spec_validator) from 2.7.4 to 2.7.5.
- [Release notes](https://github.com/Yelp/swagger_spec_validator/releases)
- [Changelog](https://github.com/Yelp/swagger_spec_validator/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/Yelp/swagger_spec_validator/compare/v2.7.4...v2.7.5)

---
updated-dependencies:
- dependency-name: swagger-spec-validator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:02:08 +02:00
d5c57ab251 core: bump sentry-sdk from 1.9.5 to 1.9.6 (#3516)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.9.5 to 1.9.6.
- [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.9.5...1.9.6)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:01:57 +02:00
5511458757 core: bump black from 22.6.0 to 22.8.0 (#3517)
Bumps [black](https://github.com/psf/black) from 22.6.0 to 22.8.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/22.6.0...22.8.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-01 10:01:41 +02:00
d9775f2822 blueprints: don't export events by default and exclude anonymous user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-31 23:32:02 +02:00
398eb23d31 blueprint: fix EntryInvalidError not being handled in tasks
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-31 23:08:38 +02:00
14a7c9f967 internal: fix outposts not logging flow execution errors correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-31 23:03:57 +02:00
3e11f0c0b3 web: bump @typescript-eslint/parser from 5.36.0 to 5.36.1 in /web (#3504)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.36.0 to 5.36.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.36.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 09:54:54 +02:00
c055245a45 web: bump @typescript-eslint/eslint-plugin from 5.36.0 to 5.36.1 in /web (#3505)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.36.0 to 5.36.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.36.1/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 09:53:29 +02:00
b1ba8f60a1 web: bump @fortawesome/fontawesome-free from 6.1.2 to 6.2.0 in /web (#3506)
Bumps [@fortawesome/fontawesome-free](https://github.com/FortAwesome/Font-Awesome) from 6.1.2 to 6.2.0.
- [Release notes](https://github.com/FortAwesome/Font-Awesome/releases)
- [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md)
- [Commits](https://github.com/FortAwesome/Font-Awesome/commits)

---
updated-dependencies:
- dependency-name: "@fortawesome/fontawesome-free"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 09:53:19 +02:00
f8f37dc52c core: bump requests-mock from 1.9.3 to 1.10.0 (#3507)
Bumps [requests-mock](https://github.com/jamielennox/requests-mock) from 1.9.3 to 1.10.0.
- [Release notes](https://github.com/jamielennox/requests-mock/releases)
- [Commits](https://github.com/jamielennox/requests-mock/compare/1.9.3...1.10.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 09:52:27 +02:00
19c36d20b5 website/docs: improve nginx examples (#3372)
* website/docs: improve nginx examples

Signed-off-by: itsmesid <693151+arevindh@users.noreply.github.com>

* website/docs: improve nginx examples

Signed-off-by: itsmesid <693151+arevindh@users.noreply.github.com>
2022-08-30 21:19:25 +02:00
abca435337 blueprints: OCI registry support (#3500)
* blueprints: add ability to load blueprints via OCI

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

* add docs

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

* fix inheritance check for meta models

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

* add oci tests

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-30 14:08:26 +02:00
b5ee81f4de web: bump typescript from 4.7.4 to 4.8.2 in /web (#3492)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.4 to 4.8.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v4.8.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-30 12:19:47 +02:00
13bb7682b2 web: bump @typescript-eslint/parser from 5.35.1 to 5.36.0 in /web (#3502)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.35.1 to 5.36.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.36.0/packages/parser)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-30 12:19:38 +02:00
99f0f556e1 web: bump @typescript-eslint/eslint-plugin from 5.35.1 to 5.36.0 in /web (#3501) 2022-08-30 09:02:41 +02:00
54ba3e9616 blueprints: add meta model to apply blueprint within blueprint for dependencies (#3486)
* add meta model to apply blueprint within blueprint for dependencies

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

* fix tests

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

* use custom registry

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

* fix again

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

* move ManagedAppConfig to apps.py

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

* rename manager to registry

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

* ci: use full tag in comment

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

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-29 21:20:58 +02:00
58e3ca28be website: fix formatting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-29 19:49:43 +02:00
c6bb41890e website/docs: add port_in_redirect in nginx config to prevent invalid port in redirect (#3397)
* Proposal and fix for issue #3359

By adding `port_in_redirect off` in the configuration for the NginxProxyManager (NPM), will avoid a redirect to port 4443.
Credit to @adtwomey for the suggestions.

https://github.com/goauthentik/authentik/issues/3359

Signed-off-by: Zolo <39656359+zolodev@users.noreply.github.com>

* Adding a comment

Signed-off-by: Zolo <39656359+zolodev@users.noreply.github.com>

Signed-off-by: Zolo <39656359+zolodev@users.noreply.github.com>
2022-08-29 17:57:18 +02:00
a4556b3692 website/docs: Added mention of how to force 2fa (#3497)
* Added mention of how to force 2fa

Added mention of how to force 2fa and fixed some punctuation's.

Signed-off-by: Joeri Colman <colmanjoeri@msn.com>

* Update website/docs/flow/examples/flows.md

Co-authored-by: Jens L. <jens@beryju.org>
Signed-off-by: Joeri Colman <colmanjoeri@msn.com>

Signed-off-by: Joeri Colman <colmanjoeri@msn.com>
Co-authored-by: Jens L. <jens@beryju.org>
2022-08-29 14:14:10 +02:00
d0b52812d5 website/docs: add mention of custom JWT Claims (#3495)
Signed-off-by: Adam Engebretson <adam@enge.me>
2022-08-29 13:11:18 +02:00
f7e689ff03 web: bump eslint from 8.22.0 to 8.23.0 in /web (#3490)
Bumps [eslint](https://github.com/eslint/eslint) from 8.22.0 to 8.23.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.22.0...v8.23.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 11:19:33 +02:00
2c419bee09 web: bump @formatjs/intl-listformat from 7.1.1 to 7.1.2 in /web (#3491)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.1.1 to 7.1.2.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.1.1...@formatjs/intl-listformat@7.1.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 11:19:25 +02:00
3c4c1e9d65 core: bump pylint from 2.14.5 to 2.15.0 (#3493)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.14.5 to 2.15.0.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.14.5...v2.15.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 11:18:58 +02:00
ef3be13fdb core: bump ua-parser from 0.16.0 to 0.16.1 (#3494)
Bumps [ua-parser](https://github.com/ua-parser/uap-python) from 0.16.0 to 0.16.1.
- [Release notes](https://github.com/ua-parser/uap-python/releases)
- [Commits](https://github.com/ua-parser/uap-python/compare/0.16.0...0.16.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-29 11:18:52 +02:00
d3466ceef8 blueprints: use correct log level when re-logging import validation logs
closes #3483

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-28 16:07:48 +02:00
5886688fae core: make request in context optional for Applications API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#3482
2022-08-28 15:59:34 +02:00
c3c8cbf7ef events: save event to test notification transport
closes #3485

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-28 15:39:42 +02:00
251ab71c3a web/user: justify content on user settings page on desktop
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-27 20:51:18 +02:00
a0c546023f Revert "web: bump typescript from 4.7.4 to 4.8.2 in /web (#3480)"
This reverts commit 2868331976.
2022-08-26 23:01:53 +02:00
83eaac375d sources/oauth: use GitHub's dedicated email API when no public email address is configured
closes #3472

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-26 21:21:41 +02:00
2868331976 web: bump typescript from 4.7.4 to 4.8.2 in /web (#3480)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.4 to 4.8.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v4.8.2)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 10:23:57 +02:00
5c0986c538 web: bump @typescript-eslint/eslint-plugin from 5.34.0 to 5.35.1 in /web (#3474)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.34.0 to 5.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.35.1/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-25 10:00:50 +02:00
ac21dcc44f web: bump @typescript-eslint/parser from 5.34.0 to 5.35.1 in /web (#3475)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.34.0 to 5.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.35.1/packages/parser)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-25 09:59:35 +02:00
e9e7e25b27 core: bump twilio from 7.12.1 to 7.13.0 (#3476)
Bumps [twilio](https://github.com/twilio/twilio-python) from 7.12.1 to 7.13.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/7.12.1...7.13.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-25 09:59:24 +02:00
7dc7d51cfa core: bump uvicorn from 0.18.2 to 0.18.3 (#3477)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.18.2 to 0.18.3.
- [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.18.2...0.18.3)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-25 09:59:04 +02:00
3eb3a9eab9 *: remove remaining default creation code in squashed migrations
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-24 23:02:34 +02:00
30db3b543b web: bump @rollup/plugin-typescript from 8.3.4 to 8.4.0 in /web (#3469)
Bumps [@rollup/plugin-typescript](https://github.com/rollup/plugins/tree/HEAD/packages/typescript) from 8.3.4 to 8.4.0.
- [Release notes](https://github.com/rollup/plugins/releases)
- [Changelog](https://github.com/rollup/plugins/blob/master/packages/typescript/CHANGELOG.md)
- [Commits](https://github.com/rollup/plugins/commits/typescript-v8.4.0/packages/typescript)

---
updated-dependencies:
- dependency-name: "@rollup/plugin-typescript"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-24 09:07:51 +02:00
11aee98e0b core: bump wsproto from 1.1.0 to 1.2.0 (#3470)
Bumps [wsproto](https://github.com/python-hyper/wsproto) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/python-hyper/wsproto/releases)
- [Changelog](https://github.com/python-hyper/wsproto/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/python-hyper/wsproto/compare/1.1.0...1.2.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-24 09:07:42 +02:00
a099b21671 lib: reset settings when error is raised in patch
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 21:21:28 +02:00
b9294fd9ad blueprints: fix unbound error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 21:15:48 +02:00
13a302cdad sources/oauth: use UPN for username with azure AD source
closes #3468
breaking

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 20:55:25 +02:00
e994a01e80 blueprints: handle blueprints without metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 20:54:56 +02:00
d49431cfc7 events: reset task info when not saving on success
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 19:22:14 +02:00
ce2ce38b59 blueprints: improve error messages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 19:21:57 +02:00
2af4f28239 stages/invitation: don't use uuid.hex
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 19:14:46 +02:00
1419910b29 blueprints: fix duplicate tasks
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 19:14:30 +02:00
649835cc61 events: fix MonitoredTasks' save_on_success not behaving as expected
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 19:13:41 +02:00
917c4ae835 ci: fix typos
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 18:49:23 +02:00
ca2fce8be2 blueprints: always set metadata when attempting to apply
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 18:48:01 +02:00
e70fcd1a6f web/admin: enable blueprint instances by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-23 18:39:22 +02:00
fd461d9a00 web: bump @formatjs/intl-listformat from 7.0.3 to 7.1.1 in /web (#3459)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 7.0.3 to 7.1.1.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@7.0.3...@formatjs/intl-listformat@7.1.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:30:07 +02:00
8ea45b4dfe core: bump ua-parser from 0.15.0 to 0.16.0 (#3461)
Bumps [ua-parser](https://github.com/ua-parser/uap-python) from 0.15.0 to 0.16.0.
- [Release notes](https://github.com/ua-parser/uap-python/releases)
- [Commits](https://github.com/ua-parser/uap-python/compare/0.15.0...0.16.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:29:57 +02:00
514d8f4569 web: bump rollup from 2.78.0 to 2.78.1 in /web (#3458)
Bumps [rollup](https://github.com/rollup/rollup) from 2.78.0 to 2.78.1.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.78.0...v2.78.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:24:47 +02:00
2a43326da5 web: bump @typescript-eslint/eslint-plugin from 5.33.1 to 5.34.0 in /web (#3465)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.33.1 to 5.34.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.34.0/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:24:35 +02:00
06f4c0608f web: bump @babel/core from 7.18.10 to 7.18.13 in /web (#3464)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.18.10 to 7.18.13.
- [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.18.13/packages/babel-core)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:23:28 +02:00
dc8b8e8f13 web: bump rollup-plugin-cssimport from 1.0.2 to 1.0.3 in /web (#3460)
Bumps rollup-plugin-cssimport from 1.0.2 to 1.0.3.

---
updated-dependencies:
- dependency-name: rollup-plugin-cssimport
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:23:10 +02:00
9f172e7ad0 web: bump @typescript-eslint/parser from 5.33.1 to 5.34.0 in /web (#3466)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.33.1 to 5.34.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.34.0/packages/parser)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:22:38 +02:00
832d76ec2a core: bump urllib3 from 1.26.11 to 1.26.12 (#3467)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.11 to 1.26.12.
- [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/1.26.11...1.26.12)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 09:22:23 +02:00
2545d85e8e core: bump xmlsec from 1.3.12 to 1.3.13 (#3462)
Bumps [xmlsec](https://github.com/mehcode/python-xmlsec) from 1.3.12 to 1.3.13.
- [Release notes](https://github.com/mehcode/python-xmlsec/releases)
- [Commits](https://github.com/mehcode/python-xmlsec/compare/1.3.12...1.3.13)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 20:48:09 +02:00
9264acd00e core: bump goauthentik.io/api/v3 from 3.2022081.1 to 3.2022082.1 (#3463)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022081.1 to 3.2022082.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022081.1...v3.2022082.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 20:47:54 +02:00
51dd9473ce web: bump API Client version (#3452) 2022-08-19 16:36:17 +01:00
532 changed files with 37657 additions and 28437 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2022.8.2
current_version = 2022.9.1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
@ -17,4 +17,4 @@ tag_name = version/{new_version}
[bumpversion:file:internal/constants/constants.go]
[bumpversion:file:web/src/constants.ts]
[bumpversion:file:web/src/common/constants.ts]

View File

@ -11,38 +11,7 @@ runs:
steps:
- name: Generate config
id: ev
shell: python
run: |
"""Helper script to get the actual branch name, docker safe"""
import os
from time import time
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
sha = "GITHUB_SHA"
branch_name = os.environ[default_branch]
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
should_build = str(os.environ.get("DOCKER_USERNAME", "") != "").lower()
print("##[set-output name=branchName]%s" % branch_name)
print(
"##[set-output name=branchNameContainer]%s"
% branch_name.replace("refs/heads/", "").replace("/", "-")
)
print("##[set-output name=timestamp]%s" % int(time()))
print("##[set-output name=sha]%s" % os.environ[sha])
print("##[set-output name=shouldBuild]%s" % should_build)
import configparser
parser = configparser.ConfigParser()
parser.read(".bumpversion.cfg")
version = parser.get("bumpversion", "current_version")
version_family = ".".join(version.split(".")[:-1])
print("##[set-output name=version]%s" % version)
print("##[set-output name=versionFamily]%s" % version_family)
uses: ./.github/actions/docker-push-variables
- name: Find Comment
uses: peter-evans/find-comment@v2
id: fc
@ -83,8 +52,6 @@ runs:
image:
repository: ghcr.io/goauthentik/dev-server
tag: ${{ inputs.tag }}
# pullPolicy: Always to ensure you always get the latest version
pullPolicy: Always
```
Afterwards, run the upgrade commands from the latest release notes.

View File

@ -27,7 +27,7 @@ runs:
docker-compose -f .github/actions/setup/docker-compose.yml up -d
poetry env use python3.10
poetry install
npm install -g pyright@1.1.136
cd web && npm ci
- name: Generate config
shell: poetry run python {0}
run: |

View File

@ -1,3 +1,4 @@
keypair
keypairs
hass
warmup

View File

@ -96,6 +96,8 @@ jobs:
testspace [unittest]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v3
with:
flags: unit
test-integration:
runs-on: ubuntu-latest
steps:
@ -117,6 +119,8 @@ jobs:
testspace [integration]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v3
with:
flags: integration
test-e2e-provider:
runs-on: ubuntu-latest
steps:
@ -139,7 +143,7 @@ jobs:
working-directory: web
run: |
npm ci
make -C .. gen-client-web
make -C .. gen-client-ts
npm run build
- name: run e2e
run: |
@ -151,6 +155,8 @@ jobs:
testspace [e2e-provider]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v3
with:
flags: e2e
test-e2e-rest:
runs-on: ubuntu-latest
steps:
@ -173,7 +179,7 @@ jobs:
working-directory: web/
run: |
npm ci
make -C .. gen-client-web
make -C .. gen-client-ts
npm run build
- name: run e2e
run: |
@ -185,6 +191,8 @@ jobs:
testspace [e2e-rest]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v3
with:
flags: e2e
ci-core-mark:
needs:
- lint
@ -240,4 +248,4 @@ jobs:
continue-on-error: true
uses: ./.github/actions/comment-pr-instructions
with:
tag: gh-${{ steps.ev.outputs.branchNameContainer }}
tag: gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.sha }}

View File

@ -23,7 +23,7 @@ jobs:
- working-directory: web/
run: npm ci
- name: Generate API
run: make gen-client-web
run: make gen-client-ts
- name: Eslint
working-directory: web/
run: npm run lint
@ -39,7 +39,7 @@ jobs:
- working-directory: web/
run: npm ci
- name: Generate API
run: make gen-client-web
run: make gen-client-ts
- name: prettier
working-directory: web/
run: npm run prettier-check
@ -60,7 +60,7 @@ jobs:
cd node_modules/@goauthentik
ln -s ../../src/ web
- name: Generate API
run: make gen-client-web
run: make gen-client-ts
- name: lit-analyse
working-directory: web/
run: npm run lit-analyse
@ -86,7 +86,7 @@ jobs:
- working-directory: web/
run: npm ci
- name: Generate API
run: make gen-client-web
run: make gen-client-ts
- name: build
working-directory: web/
run: npm run build

View File

@ -10,13 +10,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3.4.1
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
- name: Generate API Client
run: make gen-client-web
run: make gen-client-ts
- name: Publish package
working-directory: gen-ts-api/
run: |

View File

@ -19,7 +19,7 @@ WORKDIR /work/web
RUN npm ci && npm run build
# Stage 3: Poetry to requirements.txt export
FROM docker.io/python:3.10.6-slim-bullseye AS poetry-locker
FROM docker.io/python:3.10.7-slim-bullseye AS poetry-locker
WORKDIR /work
COPY ./pyproject.toml /work
@ -30,7 +30,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.19.0-bullseye AS go-builder
FROM docker.io/golang:1.19.3-bullseye AS go-builder
WORKDIR /work
@ -46,7 +46,7 @@ COPY ./go.sum /work/go.sum
RUN go build -o /work/authentik ./cmd/server/main.go
# Stage 5: Run
FROM docker.io/python:3.10.6-slim-bullseye AS final-image
FROM docker.io/python:3.10.7-slim-bullseye AS final-image
LABEL org.opencontainers.image.url https://goauthentik.io
LABEL org.opencontainers.image.description goauthentik.io Main server image, see https://goauthentik.io for more info.

View File

@ -28,7 +28,7 @@ test-docker:
rm -f .env
test:
coverage run manage.py test authentik
coverage run manage.py test --keepdb authentik
coverage html
coverage report
@ -49,28 +49,50 @@ lint:
bandit -r authentik tests lifecycle -x node_modules
golangci-lint run -v
migrate:
python -m lifecycle.migrate
run:
go run -v cmd/server/main.go
i18n-extract: i18n-extract-core web-extract
i18n-extract-core:
ak makemessages --ignore web --ignore internal --ignore web --ignore web-api --ignore website -l en
#########################
## API Schema
#########################
gen-build:
AUTHENTIK_DEBUG=true ak make_blueprint_schema > blueprints/schema.json
AUTHENTIK_DEBUG=true ak spectacular --file schema.yml
gen-diff:
git show $(shell git tag -l | tail -n 1):schema.yml > old_schema.yml
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-diff:2.0.1 \
--markdown /local/diff.md \
/local/old_schema.yml /local/schema.yml
rm old_schema.yml
gen-clean:
rm -rf web/api/src/
rm -rf api/
gen-client-web:
gen-client-ts:
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
openapitools/openapi-generator-cli:v6.0.0 generate \
docker.io/openapitools/openapi-generator-cli:v6.0.0 generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/gen-ts-api \
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=@goauthentik/api,npmVersion=${NPM_VERSION}
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=@goauthentik/api,npmVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
mkdir -p web/node_modules/@goauthentik/api
\cp -fv scripts/web_api_readme.md gen-ts-api/README.md
cd gen-ts-api && npm i
@ -84,7 +106,7 @@ gen-client-go:
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
openapitools/openapi-generator-cli:v6.0.0 generate \
docker.io/openapitools/openapi-generator-cli:v6.0.0 generate \
-i /local/schema.yml \
-g go \
-o /local/gen-go-api \
@ -95,13 +117,7 @@ gen-client-go:
gen-dev-config:
python -m scripts.generate_config
gen: gen-build gen-clean gen-client-web
migrate:
python -m lifecycle.migrate
run:
go run -v cmd/server/main.go
gen: gen-build gen-clean gen-client-ts
#########################
## Web
@ -148,25 +164,25 @@ website-watch:
# These targets are use by GitHub actions to allow usage of matrix
# which makes the YAML File a lot smaller
PY_SOURCES=authentik tests lifecycle
ci--meta-debug:
python -V
node --version
ci-pylint: ci--meta-debug
pylint authentik tests lifecycle
pylint $(PY_SOURCES)
ci-black: ci--meta-debug
black --check authentik tests lifecycle
black --check $(PY_SOURCES)
ci-isort: ci--meta-debug
isort --check authentik tests lifecycle
isort --check $(PY_SOURCES)
ci-bandit: ci--meta-debug
bandit -r authentik tests lifecycle
bandit -r $(PY_SOURCES)
ci-pyright: ci--meta-debug
pyright e2e lifecycle
./web/node_modules/.bin/pyright $(PY_SOURCES)
ci-pending-migrations: ci--meta-debug
ak makemigrations --check

View File

@ -2,7 +2,7 @@
from os import environ
from typing import Optional
__version__ = "2022.8.2"
__version__ = "2022.9.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -18,13 +18,13 @@ class AppSerializer(PassiveSerializer):
class AppsViewSet(ViewSet):
"""Read-only view set list all installed apps"""
"""Read-only view list all installed apps"""
permission_classes = [IsAdminUser]
@extend_schema(responses={200: AppSerializer(many=True)})
def list(self, request: Request) -> Response:
"""List current messages and pass into Serializer"""
"""Read-only view list all installed apps"""
data = []
for app in sorted(get_apps(), key=lambda app: app.name):
data.append({"name": app.name, "label": app.verbose_name})

View File

@ -5,7 +5,7 @@ from django.contrib import messages
from django.http.response import Http404
from django.utils.translation import gettext_lazy as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from rest_framework.decorators import action
from rest_framework.fields import CharField, ChoiceField, DateTimeField, ListField
from rest_framework.permissions import IsAdminUser
@ -58,7 +58,15 @@ class TaskViewSet(ViewSet):
responses={
200: TaskSerializer(many=False),
404: OpenApiResponse(description="Task not found"),
}
},
parameters=[
OpenApiParameter(
"id",
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH,
required=True,
),
],
)
# pylint: disable=invalid-name
def retrieve(self, request: Request, pk=None) -> Response:
@ -81,6 +89,14 @@ class TaskViewSet(ViewSet):
404: OpenApiResponse(description="Task not found"),
500: OpenApiResponse(description="Failed to retry task"),
},
parameters=[
OpenApiParameter(
"id",
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH,
required=True,
),
],
)
@action(detail=True, methods=["post"])
# pylint: disable=invalid-name

View File

@ -1,7 +1,7 @@
"""authentik admin app config"""
from prometheus_client import Gauge, Info
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
PROM_INFO = Info("authentik_version", "Currently running authentik version")
GAUGE_WORKERS = Gauge("authentik_admin_workers", "Currently connected workers")

View File

@ -16,7 +16,7 @@ from authentik.providers.oauth2.models import RefreshToken
LOGGER = get_logger()
def validate_auth(header: bytes) -> str:
def validate_auth(header: bytes) -> Optional[str]:
"""Validate that the header is in a correct format,
returns type and credentials"""
auth_credentials = header.decode().strip()

View File

@ -60,8 +60,28 @@ def postprocess_schema_responses(result, generator, **kwargs): # noqa: W0613
for path in result["paths"].values():
for method in path.values():
method["responses"].setdefault("400", validation_error.ref)
method["responses"].setdefault("403", generic_error.ref)
method["responses"].setdefault(
"400",
{
"content": {
"application/json": {
"schema": validation_error.ref,
}
},
"description": "",
},
)
method["responses"].setdefault(
"403",
{
"content": {
"application/json": {
"schema": generic_error.ref,
}
},
"description": "",
},
)
result["components"] = generator.registry.build(spectacular_settings.APPEND_COMPONENTS)

View File

@ -1,6 +1,46 @@
"""authentik Blueprints app"""
from authentik.blueprints.manager import ManagedAppConfig
from importlib import import_module
from inspect import ismethod
from django.apps import AppConfig
from django.db import DatabaseError, InternalError, ProgrammingError
from structlog.stdlib import BoundLogger, get_logger
class ManagedAppConfig(AppConfig):
"""Basic reconciliation logic for apps"""
_logger: BoundLogger
def __init__(self, app_name: str, *args, **kwargs) -> None:
super().__init__(app_name, *args, **kwargs)
self._logger = get_logger().bind(app_name=app_name)
def ready(self) -> None:
self.reconcile()
return super().ready()
def import_module(self, path: str):
"""Load module"""
import_module(path)
def reconcile(self) -> None:
"""reconcile ourselves"""
prefix = "reconcile_"
for meth_name in dir(self):
meth = getattr(self, meth_name)
if not ismethod(meth):
continue
if not meth_name.startswith(prefix):
continue
name = meth_name.replace(prefix, "")
try:
self._logger.debug("Starting reconciler", name=name)
meth()
self._logger.debug("Successfully reconciled", name=name)
except (DatabaseError, ProgrammingError, InternalError) as exc:
self._logger.debug("Failed to run reconcile", name=name, exc=exc)
class AuthentikBlueprintsConfig(ManagedAppConfig):
@ -20,3 +60,7 @@ class AuthentikBlueprintsConfig(ManagedAppConfig):
from authentik.blueprints.v1.tasks import blueprints_discover
blueprints_discover.delay()
def import_models(self):
super().import_models()
self.import_module("authentik.blueprints.v1.meta.apply_blueprint")

View File

@ -1,4 +1,6 @@
"""Apply blueprint from commandline"""
from sys import exit as sys_exit
from django.core.management.base import BaseCommand, no_translations
from structlog.stdlib import get_logger
@ -20,8 +22,9 @@ class Command(BaseCommand):
valid, logs = importer.validate()
if not valid:
for log in logs:
LOGGER.debug(**log)
raise ValueError("blueprint invalid")
getattr(LOGGER, log.pop("log_level"))(**log)
self.stderr.write("blueprint invalid")
sys_exit(1)
importer.apply()
def add_arguments(self, parser):

View File

@ -2,12 +2,11 @@
from json import dumps, loads
from pathlib import Path
from django.apps import apps
from django.core.management.base import BaseCommand, no_translations
from structlog.stdlib import get_logger
from authentik.blueprints.v1.importer import is_model_allowed
from authentik.lib.models import SerializerModel
from authentik.blueprints.v1.meta.registry import registry
LOGGER = get_logger()
@ -29,10 +28,9 @@ class Command(BaseCommand):
def set_model_allowed(self):
"""Set model enum"""
model_names = []
for model in apps.get_models():
for model in registry.get_models():
if not is_model_allowed(model):
continue
if SerializerModel not in model.__mro__:
continue
model_names.append(f"{model._meta.app_label}.{model._meta.model_name}")
model_names.sort()
self.schema["properties"]["entries"]["items"]["properties"]["model"]["enum"] = model_names

View File

@ -41,8 +41,7 @@
"$id": "#entry",
"type": "object",
"required": [
"model",
"identifiers"
"model"
],
"properties": {
"model": {
@ -67,6 +66,7 @@
},
"identifiers": {
"type": "object",
"default": {},
"properties": {
"pk": {
"description": "Commonly available field, may not exist on all models",

View File

@ -1,44 +0,0 @@
"""Managed objects manager"""
from importlib import import_module
from inspect import ismethod
from django.apps import AppConfig
from django.db import DatabaseError, InternalError, ProgrammingError
from structlog.stdlib import BoundLogger, get_logger
LOGGER = get_logger()
class ManagedAppConfig(AppConfig):
"""Basic reconciliation logic for apps"""
_logger: BoundLogger
def __init__(self, app_name: str, *args, **kwargs) -> None:
super().__init__(app_name, *args, **kwargs)
self._logger = get_logger().bind(app_name=app_name)
def ready(self) -> None:
self.reconcile()
return super().ready()
def import_module(self, path: str):
"""Load module"""
import_module(path)
def reconcile(self) -> None:
"""reconcile ourselves"""
prefix = "reconcile_"
for meth_name in dir(self):
meth = getattr(self, meth_name)
if not ismethod(meth):
continue
if not meth_name.startswith(prefix):
continue
name = meth_name.replace(prefix, "")
try:
self._logger.debug("Starting reconciler", name=name)
meth()
self._logger.debug("Successfully reconciled", name=name)
except (DatabaseError, ProgrammingError, InternalError) as exc:
self._logger.debug("Failed to run reconcile", name=name, exc=exc)

View File

@ -4,7 +4,7 @@ from glob import glob
from pathlib import Path
import django.contrib.postgres.fields
from dacite import from_dict
from dacite.core import from_dict
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models

View File

@ -1,4 +1,4 @@
"""Managed Object models"""
"""blueprint models"""
from pathlib import Path
from urllib.parse import urlparse
from uuid import uuid4
@ -6,13 +6,26 @@ from uuid import uuid4
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.translation import gettext_lazy as _
from requests import RequestException
from opencontainers.distribution.reggie import (
NewClient,
WithDebug,
WithDefaultName,
WithDigest,
WithReference,
WithUserAgent,
WithUsernamePassword,
)
from requests.exceptions import RequestException
from rest_framework.serializers import Serializer
from structlog import get_logger
from authentik.lib.config import CONFIG
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_http_session
from authentik.lib.utils.http import authentik_user_agent
OCI_MEDIA_TYPE = "application/vnd.goauthentik.blueprint.v1+yaml"
LOGGER = get_logger()
class BlueprintRetrievalFailed(SentryIgnoredException):
@ -71,18 +84,63 @@ class BlueprintInstance(SerializerModel, ManagedModel, CreatedUpdatedModel):
enabled = models.BooleanField(default=True)
managed_models = ArrayField(models.TextField(), default=list)
def retrieve_oci(self) -> str:
"""Get blueprint from an OCI registry"""
url = urlparse(self.path)
ref = "latest"
path = url.path[1:]
if ":" in url.path:
path, _, ref = path.partition(":")
client = NewClient(
f"{url.scheme}://{url.hostname}",
WithUserAgent(authentik_user_agent()),
WithUsernamePassword(url.username, url.password),
WithDefaultName(path),
WithDebug(True),
)
LOGGER.info("Fetching OCI manifests for blueprint", instance=self)
manifest_request = client.NewRequest(
"GET",
"/v2/<name>/manifests/<reference>",
WithReference(ref),
).SetHeader("Accept", "application/vnd.oci.image.manifest.v1+json")
try:
manifest_response = client.Do(manifest_request)
manifest_response.raise_for_status()
except RequestException as exc:
raise BlueprintRetrievalFailed(exc) from exc
manifest = manifest_response.json()
if "errors" in manifest:
raise BlueprintRetrievalFailed(manifest["errors"])
blob = None
for layer in manifest.get("layers", []):
if layer.get("mediaType", "") == OCI_MEDIA_TYPE:
blob = layer.get("digest")
LOGGER.debug("Found layer with matching media type", instance=self, blob=blob)
if not blob:
raise BlueprintRetrievalFailed("Blob not found")
blob_request = client.NewRequest(
"GET",
"/v2/<name>/blobs/<digest>",
WithDigest(blob),
)
try:
blob_response = client.Do(blob_request)
blob_response.raise_for_status()
return blob_response.text
except RequestException as exc:
raise BlueprintRetrievalFailed(exc) from exc
def retrieve(self) -> str:
"""Retrieve blueprint contents"""
if urlparse(self.path).scheme != "":
try:
res = get_http_session().get(self.path, timeout=3, allow_redirects=True)
res.raise_for_status()
return res.text
except RequestException as exc:
raise BlueprintRetrievalFailed(exc) from exc
path = Path(CONFIG.y("blueprints_dir")).joinpath(Path(self.path))
with path.open("r", encoding="utf-8") as _file:
return _file.read()
full_path = Path(CONFIG.y("blueprints_dir")).joinpath(Path(self.path))
if full_path.exists():
LOGGER.debug("Blueprint path exists locally", instance=self)
with full_path.open("r", encoding="utf-8") as _file:
return _file.read()
return self.retrieve_oci()
@property
def serializer(self) -> Serializer:

View File

@ -5,7 +5,7 @@ from typing import Callable
from django.apps import apps
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
from authentik.blueprints.models import BlueprintInstance
from authentik.lib.config import CONFIG

View File

@ -0,0 +1,97 @@
"""Test blueprints OCI"""
from django.test import TransactionTestCase
from requests_mock import Mocker
from authentik.blueprints.models import OCI_MEDIA_TYPE, BlueprintInstance, BlueprintRetrievalFailed
class TestBlueprintOCI(TransactionTestCase):
"""Test Blueprints OCI Tasks"""
def test_successful(self):
"""Successful retrieval"""
with Mocker() as mocker:
mocker.get(
"https://ghcr.io/v2/goauthentik/blueprints/test/manifests/latest",
json={
"layers": [
{
"mediaType": OCI_MEDIA_TYPE,
"digest": "foo",
}
]
},
)
mocker.get("https://ghcr.io/v2/goauthentik/blueprints/test/blobs/foo", text="foo")
self.assertEqual(
BlueprintInstance(
path="https://ghcr.io/goauthentik/blueprints/test:latest"
).retrieve_oci(),
"foo",
)
def test_manifests_error(self):
"""Test manifests request erroring"""
with Mocker() as mocker:
mocker.get(
"https://ghcr.io/v2/goauthentik/blueprints/test/manifests/latest", status_code=401
)
with self.assertRaises(BlueprintRetrievalFailed):
BlueprintInstance(
path="https://ghcr.io/goauthentik/blueprints/test:latest"
).retrieve_oci()
def test_manifests_error_response(self):
"""Test manifests request erroring"""
with Mocker() as mocker:
mocker.get(
"https://ghcr.io/v2/goauthentik/blueprints/test/manifests/latest",
json={"errors": ["foo"]},
)
with self.assertRaises(BlueprintRetrievalFailed):
BlueprintInstance(
path="https://ghcr.io/goauthentik/blueprints/test:latest"
).retrieve_oci()
def test_no_matching_blob(self):
"""Successful retrieval"""
with Mocker() as mocker:
mocker.get(
"https://ghcr.io/v2/goauthentik/blueprints/test/manifests/latest",
json={
"layers": [
{
"mediaType": OCI_MEDIA_TYPE + "foo",
"digest": "foo",
}
]
},
)
with self.assertRaises(BlueprintRetrievalFailed):
BlueprintInstance(
path="https://ghcr.io/goauthentik/blueprints/test:latest"
).retrieve_oci()
def test_blob_error(self):
"""Successful retrieval"""
with Mocker() as mocker:
mocker.get(
"https://ghcr.io/v2/goauthentik/blueprints/test/manifests/latest",
json={
"layers": [
{
"mediaType": OCI_MEDIA_TYPE,
"digest": "foo",
}
]
},
)
mocker.get("https://ghcr.io/v2/goauthentik/blueprints/test/blobs/foo", status_code=401)
with self.assertRaises(BlueprintRetrievalFailed):
BlueprintInstance(
path="https://ghcr.io/goauthentik/blueprints/test:latest"
).retrieve_oci()

View File

@ -33,4 +33,6 @@ def blueprint_tester(file_name: Path) -> Callable:
for blueprint_file in Path("blueprints/").glob("**/*.yaml"):
if "local" in str(blueprint_file):
continue
setattr(TestPackaged, f"test_blueprint_{blueprint_file}", blueprint_tester(blueprint_file))

View File

@ -45,8 +45,8 @@ class BlueprintEntryState:
class BlueprintEntry:
"""Single entry of a blueprint"""
identifiers: dict[str, Any]
model: str
identifiers: dict[str, Any] = field(default_factory=dict)
attrs: Optional[dict[str, Any]] = field(default_factory=dict)
# pylint: disable=invalid-name
@ -105,9 +105,9 @@ class Blueprint:
version: int = field(default=1)
entries: list[BlueprintEntry] = field(default_factory=list)
context: dict = field(default_factory=dict)
metadata: Optional[BlueprintMetadata] = field(default=None)
context: Optional[dict] = field(default_factory=dict)
class YAMLTag:
@ -253,3 +253,9 @@ class BlueprintLoader(SafeLoader):
class EntryInvalidError(SentryIgnoredException):
"""Error raised when an entry is invalid"""
serializer_errors: Optional[dict]
def __init__(self, *args: object, serializer_errors: Optional[dict] = None) -> None:
super().__init__(*args)
self.serializer_errors = serializer_errors

View File

@ -1,11 +1,13 @@
"""Blueprint exporter"""
from typing import Iterator
from typing import Iterable
from uuid import UUID
from django.apps import apps
from django.db.models import Q
from django.contrib.auth import get_user_model
from django.db.models import Model, Q, QuerySet
from django.utils.timezone import now
from django.utils.translation import gettext as _
from guardian.shortcuts import get_anonymous_user
from yaml import dump
from authentik.blueprints.v1.common import (
@ -16,8 +18,8 @@ from authentik.blueprints.v1.common import (
)
from authentik.blueprints.v1.importer import is_model_allowed
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_GENERATED
from authentik.events.models import Event
from authentik.flows.models import Flow, FlowStageBinding, Stage
from authentik.lib.models import SerializerModel
from authentik.policies.models import Policy, PolicyBinding
from authentik.stages.prompt.models import PromptStage
@ -25,23 +27,30 @@ from authentik.stages.prompt.models import PromptStage
class Exporter:
"""Export flow with attached stages into yaml"""
excluded_models = []
excluded_models: list[type[Model]] = []
def __init__(self):
self.excluded_models = []
self.excluded_models = [
Event,
]
def get_entries(self) -> Iterator[BlueprintEntry]:
def get_entries(self) -> Iterable[BlueprintEntry]:
"""Get blueprint entries"""
for model in apps.get_models():
if not is_model_allowed(model):
continue
if model in self.excluded_models:
continue
if SerializerModel not in model.__mro__:
continue
for obj in model.objects.all():
for obj in self.get_model_instances(model):
yield BlueprintEntry.from_model(obj)
def get_model_instances(self, model: type[Model]) -> QuerySet:
"""Return a queryset for `model`. Can be used to filter some
objects on some models"""
if model == get_user_model():
return model.objects.exclude(pk=get_anonymous_user().pk)
return model.objects.all()
def _pre_export(self, blueprint: Blueprint):
"""Hook to run anything pre-export"""
@ -87,7 +96,7 @@ class FlowExporter(Exporter):
"pbm_uuid", flat=True
)
def walk_stages(self) -> Iterator[BlueprintEntry]:
def walk_stages(self) -> Iterable[BlueprintEntry]:
"""Convert all stages attached to self.flow into BlueprintEntry objects"""
stages = Stage.objects.filter(flow=self.flow).select_related().select_subclasses()
for stage in stages:
@ -95,13 +104,13 @@ class FlowExporter(Exporter):
pass
yield BlueprintEntry.from_model(stage, "name")
def walk_stage_bindings(self) -> Iterator[BlueprintEntry]:
def walk_stage_bindings(self) -> Iterable[BlueprintEntry]:
"""Convert all bindings attached to self.flow into BlueprintEntry objects"""
bindings = FlowStageBinding.objects.filter(target=self.flow).select_related()
for binding in bindings:
yield BlueprintEntry.from_model(binding, "target", "stage", "order")
def walk_policies(self) -> Iterator[BlueprintEntry]:
def walk_policies(self) -> Iterable[BlueprintEntry]:
"""Walk over all policies. This is done at the beginning of the export for stages that have
a direct foreign key to a policy."""
# Special case for PromptStage as that has a direct M2M to policy, we have to ensure
@ -112,21 +121,21 @@ class FlowExporter(Exporter):
for policy in policies:
yield BlueprintEntry.from_model(policy)
def walk_policy_bindings(self) -> Iterator[BlueprintEntry]:
def walk_policy_bindings(self) -> Iterable[BlueprintEntry]:
"""Walk over all policybindings relative to us. This is run at the end of the export, as
we are sure all objects exist now."""
bindings = PolicyBinding.objects.filter(target__in=self.pbm_uuids).select_related()
for binding in bindings:
yield BlueprintEntry.from_model(binding, "policy", "target", "order")
def walk_stage_prompts(self) -> Iterator[BlueprintEntry]:
def walk_stage_prompts(self) -> Iterable[BlueprintEntry]:
"""Walk over all prompts associated with any PromptStages"""
prompt_stages = PromptStage.objects.filter(flow=self.flow)
for stage in prompt_stages:
for prompt in stage.fields.all():
yield BlueprintEntry.from_model(prompt)
def get_entries(self) -> Iterator[BlueprintEntry]:
def get_entries(self) -> Iterable[BlueprintEntry]:
entries = []
entries.append(BlueprintEntry.from_model(self.flow, "slug"))
if self.with_stage_prompts:

View File

@ -3,10 +3,9 @@ from contextlib import contextmanager
from copy import deepcopy
from typing import Any, Optional
from dacite import from_dict
from dacite.core import from_dict
from dacite.exceptions import DaciteError
from deepmerge import always_merger
from django.apps import apps
from django.db import transaction
from django.db.models import Model
from django.db.models.query_utils import Q
@ -25,6 +24,7 @@ from authentik.blueprints.v1.common import (
BlueprintLoader,
EntryInvalidError,
)
from authentik.blueprints.v1.meta.registry import BaseMetaModel, registry
from authentik.core.models import (
AuthenticatedSession,
PropertyMapping,
@ -59,7 +59,7 @@ def is_model_allowed(model: type[Model]) -> bool:
# Classes that have other dependencies
AuthenticatedSession,
)
return model not in excluded_models
return model not in excluded_models and issubclass(model, (SerializerModel, BaseMetaModel))
@contextmanager
@ -138,10 +138,20 @@ class Importer:
def _validate_single(self, entry: BlueprintEntry) -> BaseSerializer:
"""Validate a single entry"""
model_app_label, model_name = entry.model.split(".")
model: type[SerializerModel] = apps.get_model(model_app_label, model_name)
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)
# Don't use isinstance since we don't want to check for inheritance
if not is_model_allowed(model):
raise EntryInvalidError(f"Model {model} not allowed")
if issubclass(model, BaseMetaModel):
serializer_class: type[Serializer] = model.serializer()
serializer = serializer_class(data=entry.get_attrs(self.__import))
try:
serializer.is_valid(raise_exception=True)
except ValidationError as exc:
raise EntryInvalidError(
f"Serializer errors {serializer.errors}", serializer_errors=serializer.errors
) from exc
return serializer
if entry.identifiers == {}:
raise EntryInvalidError("No identifiers")
@ -158,7 +168,7 @@ class Importer:
existing_models = model.objects.filter(self.__query_from_identifier(updated_identifiers))
serializer_kwargs = {}
if existing_models.exists():
if not isinstance(model(), BaseMetaModel) and existing_models.exists():
model_instance = existing_models.first()
self.logger.debug(
"initialise serializer with instance",
@ -169,7 +179,9 @@ class Importer:
serializer_kwargs["instance"] = model_instance
serializer_kwargs["partial"] = True
else:
self.logger.debug("initialise new instance", model=model, **updated_identifiers)
self.logger.debug(
"initialised new serializer instance", model=model, **updated_identifiers
)
model_instance = model()
# pk needs to be set on the model instance otherwise a new one will be generated
if "pk" in updated_identifiers:
@ -183,7 +195,9 @@ class Importer:
try:
serializer.is_valid(raise_exception=True)
except ValidationError as exc:
raise EntryInvalidError(f"Serializer errors {serializer.errors}") from exc
raise EntryInvalidError(
f"Serializer errors {serializer.errors}", serializer_errors=serializer.errors
) from exc
return serializer
def apply(self) -> bool:
@ -205,7 +219,7 @@ class Importer:
for entry in self.__import.entries:
model_app_label, model_name = entry.model.split(".")
try:
model: SerializerModel = apps.get_model(model_app_label, model_name)
model: type[SerializerModel] = registry.get_model(model_app_label, model_name)
except LookupError:
self.logger.warning(
"app or model does not exist", app=model_app_label, model=model_name
@ -215,14 +229,14 @@ class Importer:
try:
serializer = self._validate_single(entry)
except EntryInvalidError as exc:
self.logger.warning("entry invalid", entry=entry, error=exc)
self.logger.warning(f"entry invalid: {exc}", entry=entry, error=exc)
return False
model = serializer.save()
if "pk" in entry.identifiers:
self.__pk_map[entry.identifiers["pk"]] = model.pk
entry._state = BlueprintEntryState(model)
self.logger.debug("updated model", model=model, pk=model.pk)
self.logger.debug("updated model", model=model)
return True
def validate(self) -> tuple[bool, list[EventDict]]:
@ -239,8 +253,8 @@ class Importer:
):
successful = self._apply_models()
if not successful:
self.logger.debug("blueprint validation failed")
self.logger.debug("Blueprint validation failed")
for log in logs:
self.logger.debug(**log)
getattr(self.logger, log.get("log_level"))(**log)
self.__import = orig_import
return successful, logs

View File

View File

@ -0,0 +1,60 @@
"""Apply Blueprint meta model"""
from typing import TYPE_CHECKING
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, JSONField
from structlog.stdlib import get_logger
from authentik.blueprints.v1.meta.registry import BaseMetaModel, MetaResult, registry
from authentik.core.api.utils import PassiveSerializer, is_dict
if TYPE_CHECKING:
from authentik.blueprints.models import BlueprintInstance
LOGGER = get_logger()
class ApplyBlueprintMetaSerializer(PassiveSerializer):
"""Serializer for meta apply blueprint model"""
identifiers = JSONField(validators=[is_dict])
required = BooleanField(default=True)
# We cannot override `instance` as that will confuse rest_framework
# and make it attempt to update the instance
blueprint_instance: "BlueprintInstance"
def validate(self, attrs):
from authentik.blueprints.models import BlueprintInstance
identifiers = attrs["identifiers"]
required = attrs["required"]
instance = BlueprintInstance.objects.filter(**identifiers).first()
if not instance and required:
raise ValidationError("Required blueprint does not exist")
self.blueprint_instance = instance
return super().validate(attrs)
def create(self, validated_data: dict) -> MetaResult:
from authentik.blueprints.v1.tasks import apply_blueprint
if not self.blueprint_instance:
LOGGER.info("Blueprint does not exist, but not required")
return MetaResult()
LOGGER.debug("Applying blueprint from meta model", blueprint=self.blueprint_instance)
# pylint: disable=no-value-for-parameter
apply_blueprint(str(self.blueprint_instance.pk))
return MetaResult()
@registry.register("metaapplyblueprint")
class MetaApplyBlueprint(BaseMetaModel):
"""Meta model to apply another blueprint"""
@staticmethod
def serializer() -> ApplyBlueprintMetaSerializer:
return ApplyBlueprintMetaSerializer
class Meta:
abstract = True

View File

@ -0,0 +1,61 @@
"""Base models"""
from django.apps import apps
from django.db.models import Model
from rest_framework.serializers import Serializer
class BaseMetaModel(Model):
"""Base models"""
@staticmethod
def serializer() -> Serializer:
"""Serializer similar to SerializerModel, but as a static method since
this is an abstract model"""
raise NotImplementedError
class Meta:
abstract = True
class MetaResult:
"""Result returned by Meta Models' serializers. Empty class but we can't return none as
the framework doesn't allow that"""
class MetaModelRegistry:
"""Registry for pseudo meta models"""
models: dict[str, BaseMetaModel]
virtual_prefix: str
def __init__(self, prefix: str) -> None:
self.models = {}
self.virtual_prefix = prefix
def register(self, model_id: str):
"""Register model class under `model_id`"""
def inner_wrapper(cls):
self.models[model_id] = cls
return cls
return inner_wrapper
def get_models(self):
"""Wrapper for django's `get_models` to list all models"""
models = apps.get_models()
for _, value in self.models.items():
models.append(value)
return models
def get_model(self, app_label: str, model_id: str) -> type[Model]:
"""Get model checks if any virtual models are registered, and falls back
to actual django models"""
if app_label.lower() == self.virtual_prefix:
if model_id.lower() in self.models:
return self.models[model_id]
return apps.get_model(app_label, model_id)
registry = MetaModelRegistry("authentik_blueprints")

View File

@ -4,8 +4,9 @@ from hashlib import sha512
from pathlib import Path
from typing import Optional
from dacite import from_dict
from dacite.core import from_dict
from django.db import DatabaseError, InternalError, ProgrammingError
from django.utils.text import slugify
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from structlog.stdlib import get_logger
@ -17,7 +18,7 @@ from authentik.blueprints.models import (
BlueprintInstanceStatus,
BlueprintRetrievalFailed,
)
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata, EntryInvalidError
from authentik.blueprints.v1.importer import Importer
from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_INSTANTIATE
from authentik.events.monitored_tasks import (
@ -76,7 +77,9 @@ def blueprints_find():
LOGGER.warning("invalid blueprint version", version=version, path=str(path))
continue
file_hash = sha512(path.read_bytes()).hexdigest()
blueprint = BlueprintFile(path.relative_to(root), version, file_hash, path.stat().st_mtime)
blueprint = BlueprintFile(
str(path.relative_to(root)), version, file_hash, int(path.stat().st_mtime)
)
blueprint.meta = from_dict(BlueprintMetadata, metadata) if metadata else None
blueprints.append(blueprint)
LOGGER.info(
@ -125,9 +128,7 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
)
instance.save()
if instance.last_applied_hash != blueprint.hash:
instance.metadata = asdict(blueprint.meta) if blueprint.meta else {}
instance.save()
apply_blueprint.delay(instance.pk.hex)
apply_blueprint.delay(str(instance.pk))
@CELERY_APP.task(
@ -136,15 +137,18 @@ def check_blueprint_v1_file(blueprint: BlueprintFile):
)
def apply_blueprint(self: MonitoredTask, instance_pk: str):
"""Apply single blueprint"""
self.set_uid(instance_pk)
self.save_on_success = False
instance: Optional[BlueprintInstance] = None
try:
instance: BlueprintInstance = BlueprintInstance.objects.filter(pk=instance_pk).first()
self.set_uid(slugify(instance.name))
if not instance or not instance.enabled:
return
blueprint_content = instance.retrieve()
file_hash = sha512(blueprint_content.encode()).hexdigest()
importer = Importer(blueprint_content, instance.context)
if importer.blueprint.metadata:
instance.metadata = asdict(importer.blueprint.metadata)
valid, logs = importer.validate()
if not valid:
instance.status = BlueprintInstanceStatus.ERROR
@ -160,7 +164,6 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
instance.status = BlueprintInstanceStatus.SUCCESSFUL
instance.last_applied_hash = file_hash
instance.last_applied = now()
instance.save()
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
except (
DatabaseError,
@ -168,7 +171,11 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
InternalError,
IOError,
BlueprintRetrievalFailed,
EntryInvalidError,
) as exc:
instance.status = BlueprintInstanceStatus.ERROR
instance.save()
if instance:
instance.status = BlueprintInstanceStatus.ERROR
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
finally:
if instance:
instance.save()

View File

@ -50,7 +50,9 @@ class ApplicationSerializer(ModelSerializer):
def get_launch_url(self, app: Application) -> Optional[str]:
"""Allow formatting of launch URL"""
user = self.context["request"].user
user = None
if "request" in self.context:
user = self.context["request"].user
return app.get_launch_url(user)
class Meta:

View File

@ -17,7 +17,7 @@ from authentik.api.decorators import permission_required
from authentik.blueprints.api import ManagedSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import MetaNameSerializer, PassiveSerializer, TypeCreateSerializer
from authentik.core.expression import PropertyMappingEvaluator
from authentik.core.expression.evaluator import PropertyMappingEvaluator
from authentik.core.models import PropertyMapping
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.api.exec import PolicyTestSerializer
@ -41,7 +41,9 @@ class PropertyMappingSerializer(ManagedSerializer, ModelSerializer, MetaNameSeri
def validate_expression(self, expression: str) -> str:
"""Test Syntax"""
evaluator = PropertyMappingEvaluator()
evaluator = PropertyMappingEvaluator(
self.instance,
)
evaluator.validate(expression)
return expression

View File

@ -1,7 +1,7 @@
"""authentik core app config"""
from django.conf import settings
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
class AuthentikCoreConfig(ManagedAppConfig):

View File

View File

@ -2,28 +2,33 @@
from traceback import format_tb
from typing import Optional
from django.db.models import Model
from django.http import HttpRequest
from guardian.utils import get_anonymous_user
from authentik.core.models import PropertyMapping, User
from authentik.core.models import User
from authentik.events.models import Event, EventAction
from authentik.lib.expression.evaluator import BaseEvaluator
from authentik.policies.types import PolicyRequest
class PropertyMappingEvaluator(BaseEvaluator):
"""Custom Evalautor that adds some different context variables."""
"""Custom Evaluator that adds some different context variables."""
def set_context(
def __init__(
self,
user: Optional[User],
request: Optional[HttpRequest],
mapping: PropertyMapping,
model: Model,
user: Optional[User] = None,
request: Optional[HttpRequest] = None,
**kwargs,
):
"""Update context with context from PropertyMapping's evaluate"""
if hasattr(model, "name"):
_filename = model.name
else:
_filename = str(model)
super().__init__(filename=_filename)
req = PolicyRequest(user=get_anonymous_user())
req.obj = mapping
req.obj = model
if user:
req.user = user
self._context["user"] = user

View File

@ -7,9 +7,9 @@ from django.core.management.base import BaseCommand
from django.db.models import Model
from django.db.models.signals import post_save, pre_delete
from authentik import __version__
from authentik import get_full_version
from authentik.core.models import User
from authentik.events.middleware import IGNORED_MODELS
from authentik.events.middleware import should_log_model
from authentik.events.models import Event, EventAction
from authentik.events.utils import model_to_dict
@ -18,7 +18,7 @@ BANNER_TEXT = """### authentik shell ({authentik})
node=platform.node(),
python=platform.python_version(),
arch=platform.machine(),
authentik=__version__,
authentik=get_full_version(),
)
@ -50,7 +50,7 @@ class Command(BaseCommand):
# pylint: disable=unused-argument
def post_save_handler(sender, instance: Model, created: bool, **_):
"""Signal handler for all object's post_save"""
if isinstance(instance, IGNORED_MODELS):
if not should_log_model(instance):
return
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
@ -66,7 +66,7 @@ class Command(BaseCommand):
# pylint: disable=unused-argument
def pre_delete_handler(sender, instance: Model, **_):
"""Signal handler for all object's pre_delete"""
if isinstance(instance, IGNORED_MODELS): # pragma: no cover
if not should_log_model(instance): # pragma: no cover
return
Event.new(EventAction.MODEL_DELETED, model=model_to_dict(instance)).set_user(

View File

@ -1,6 +1,6 @@
"""authentik admin Middleware to impersonate users"""
from contextvars import ContextVar
from typing import Callable
from typing import Callable, Optional
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
@ -13,9 +13,9 @@ RESPONSE_HEADER_ID = "X-authentik-id"
KEY_AUTH_VIA = "auth_via"
KEY_USER = "user"
CTX_REQUEST_ID = ContextVar(STRUCTLOG_KEY_PREFIX + "request_id", default=None)
CTX_HOST = ContextVar(STRUCTLOG_KEY_PREFIX + "host", default=None)
CTX_AUTH_VIA = ContextVar(STRUCTLOG_KEY_PREFIX + KEY_AUTH_VIA, default=None)
CTX_REQUEST_ID = ContextVar[Optional[str]](STRUCTLOG_KEY_PREFIX + "request_id", default=None)
CTX_HOST = ContextVar[Optional[str]](STRUCTLOG_KEY_PREFIX + "host", default=None)
CTX_AUTH_VIA = ContextVar[Optional[str]](STRUCTLOG_KEY_PREFIX + KEY_AUTH_VIA, default=None)
class ImpersonateMiddleware:

View File

@ -617,10 +617,9 @@ class PropertyMapping(SerializerModel, ManagedModel):
def evaluate(self, user: Optional[User], request: Optional[HttpRequest], **kwargs) -> Any:
"""Evaluate `self.expression` using `**kwargs` as Context."""
from authentik.core.expression import PropertyMappingEvaluator
from authentik.core.expression.evaluator import PropertyMappingEvaluator
evaluator = PropertyMappingEvaluator()
evaluator.set_context(user, request, self, **kwargs)
evaluator = PropertyMappingEvaluator(self, user, request, **kwargs)
try:
return evaluator.evaluate(self.expression)
except Exception as exc:

View File

@ -1,31 +0,0 @@
{% extends 'base/skeleton.html' %}
{% load i18n %}
{% block head %}
{{ block.super }}
<style>
.pf-c-empty-state {
height: 100vh;
}
</style>
{% endblock %}
{% block body %}
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
<div class="pf-c-empty-state">
<div class="pf-c-empty-state__content">
<i class="fas fa-exclamation-circle pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">
{% trans title %}
</h1>
<div class="pf-c-empty-state__body">
{% if message %}
<h3>{% trans message %}</h3>
{% endif %}
</div>
<a href="/" class="pf-c-button pf-m-primary pf-m-block">{% trans 'Go to home' %}</a>
</div>
</div>
</section>
{% endblock %}

View File

@ -4,9 +4,11 @@
{% load i18n %}
{% block head %}
<script src="{% static 'dist/admin/AdminInterface.js' %}" type="module"></script>
<script src="{% static 'dist/admin/AdminInterface.js' %}?version={{ version }}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<link rel="icon" href="{{ tenant.branding_favicon }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
<script>
window.authentik = {};
window.authentik.locale = "{{ tenant.default_locale }}";

View File

@ -0,0 +1,21 @@
{% extends 'login/base_full.html' %}
{% load static %}
{% load i18n %}
{% block title %}
{% trans 'End session' %} - {{ tenant.branding_title }}
{% endblock %}
{% block card_title %}
{% trans title %}
{% endblock %}
{% block card %}
<form method="POST" class="pf-c-form">
<p>{% trans message %}</p>
<a id="ak-back-home" href="{% url 'authentik_core:root-redirect' %}" class="pf-c-button pf-m-primary">
{% trans 'Go home' %}
</a>
</form>
{% endblock %}

View File

@ -6,6 +6,8 @@
{% block head_before %}
{{ block.super }}
<link rel="prefetch" href="{{ flow.background_url }}" />
<link rel="icon" href="{{ tenant.branding_favicon }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
{% if flow.compatibility_mode and not inspector %}
<script>ShadyDOM = { force: !navigator.webdriver };</script>
{% endif %}
@ -21,7 +23,7 @@ window.authentik.flow = {
{% endblock %}
{% block head %}
<script src="{% static 'dist/flow/FlowInterface.js' %}" type="module"></script>
<script src="{% static 'dist/flow/FlowInterface.js' %}?version={{ version }}" type="module"></script>
<style>
:root {
--ak-flow-background: url("{{ flow.background_url }}");

View File

@ -4,9 +4,11 @@
{% load i18n %}
{% block head %}
<script src="{% static 'dist/user/UserInterface.js' %}" type="module"></script>
<script src="{% static 'dist/user/UserInterface.js' %}?version={{ version }}" type="module"></script>
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: dark)">
<link rel="icon" href="{{ tenant.branding_favicon }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
<script>
window.authentik = {};
window.authentik.locale = "{{ tenant.default_locale }}";

View File

@ -5,8 +5,7 @@ from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.models import OAuth2Provider
@ -20,10 +19,7 @@ class TestApplicationsAPI(APITestCase):
self.provider = OAuth2Provider.objects.create(
name="test",
redirect_uris="http://some-other-domain",
authorization_flow=Flow.objects.create(
name="test",
slug="test",
),
authorization_flow=create_test_flow(),
)
self.allowed = Application.objects.create(
name="allowed",

View File

@ -4,8 +4,7 @@ from unittest.mock import MagicMock, patch
from django.urls import reverse
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_tenant
from authentik.flows.models import Flow, FlowDesignation
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant
from authentik.flows.tests import FlowTestCase
from authentik.tenants.models import Tenant
@ -21,11 +20,7 @@ class TestApplicationsViews(FlowTestCase):
def test_check_redirect(self):
"""Test redirect"""
empty_flow = Flow.objects.create(
name="foo",
slug="foo",
designation=FlowDesignation.AUTHENTICATION,
)
empty_flow = create_test_flow()
tenant: Tenant = create_test_tenant()
tenant.flow_authentication = empty_flow
tenant.save()
@ -49,11 +44,7 @@ class TestApplicationsViews(FlowTestCase):
def test_check_redirect_auth(self):
"""Test redirect"""
self.client.force_login(self.user)
empty_flow = Flow.objects.create(
name="foo",
slug="foo",
designation=FlowDesignation.AUTHENTICATION,
)
empty_flow = create_test_flow()
tenant: Tenant = create_test_tenant()
tenant.flow_authentication = empty_flow
tenant.save()

View File

@ -6,7 +6,7 @@ from guardian.utils import get_anonymous_user
from authentik.core.models import SourceUserMatchingModes, User
from authentik.core.sources.flow_manager import Action
from authentik.flows.models import Flow, FlowDesignation
from authentik.core.tests.utils import create_test_flow
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.policies.denied import AccessDeniedResponse
@ -152,9 +152,7 @@ class TestSourceFlowManager(TestCase):
"""Test error handling when a source selected flow is non-applicable due to a policy"""
self.source.user_matching_mode = SourceUserMatchingModes.USERNAME_LINK
flow = Flow.objects.create(
name="test", slug="test", title="test", designation=FlowDesignation.ENROLLMENT
)
flow = create_test_flow()
policy = ExpressionPolicy.objects.create(
name="false", expression="""ak_message("foo");return False"""
)

View File

@ -159,7 +159,6 @@ class TestUsersAPI(APITestCase):
response = self.client.get(
reverse("authentik_api:user-paths"),
)
print(response.content)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {"paths": ["users"]})

View File

@ -52,5 +52,5 @@ def create_test_cert() -> CertificateKeyPair:
subject_alt_names=["goauthentik.io"],
validity_days=360,
)
builder.name = generate_id()
builder.common_name = generate_id()
return builder.save()

View File

@ -32,7 +32,7 @@ class BadRequestView(TemplateView):
extra_context = {"title": "Bad Request"}
response_class = BadRequestTemplateResponse
template_name = "error/generic.html"
template_name = "if/error.html"
class ForbiddenView(TemplateView):
@ -41,7 +41,7 @@ class ForbiddenView(TemplateView):
extra_context = {"title": "Forbidden"}
response_class = ForbiddenTemplateResponse
template_name = "error/generic.html"
template_name = "if/error.html"
class NotFoundView(TemplateView):
@ -50,7 +50,7 @@ class NotFoundView(TemplateView):
extra_context = {"title": "Not Found"}
response_class = NotFoundTemplateResponse
template_name = "error/generic.html"
template_name = "if/error.html"
class ServerErrorView(TemplateView):
@ -59,7 +59,7 @@ class ServerErrorView(TemplateView):
extra_context = {"title": "Server Error"}
response_class = ServerErrorTemplateResponse
template_name = "error/generic.html"
template_name = "if/error.html"
# pylint: disable=useless-super-delegation
def dispatch(self, *args, **kwargs): # pragma: no cover

View File

@ -12,10 +12,11 @@ from django_filters.filters import BooleanFilter
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, DateTimeField, IntegerField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, ValidationError
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger

View File

@ -2,7 +2,7 @@
from datetime import datetime
from typing import TYPE_CHECKING, Optional
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.generators import generate_id
if TYPE_CHECKING:

View File

@ -26,7 +26,7 @@ class CertificateBuilder:
self.common_name = "authentik Self-signed Certificate"
self.cert = CertificateKeyPair()
def save(self) -> Optional[CertificateKeyPair]:
def save(self) -> CertificateKeyPair:
"""Save generated certificate as model"""
if not self.__certificate:
raise ValueError("Certificated hasn't been built yet")

View File

@ -0,0 +1,51 @@
"""Import certificate"""
from sys import exit as sys_exit
from django.core.management.base import BaseCommand, no_translations
from rest_framework.exceptions import ValidationError
from structlog.stdlib import get_logger
from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.models import CertificateKeyPair
LOGGER = get_logger()
class Command(BaseCommand):
"""Import certificate"""
@no_translations
def handle(self, *args, **options):
"""Import certificate"""
keypair = CertificateKeyPair.objects.filter(name=options["name"]).first()
dirty = False
if not keypair:
keypair = CertificateKeyPair(name=options["name"])
dirty = True
with open(options["certificate"], mode="r", encoding="utf-8") as _cert:
cert_data = _cert.read()
if keypair.certificate_data != cert_data:
dirty = True
keypair.certificate_data = cert_data
if options["private_key"]:
with open(options["private_key"], mode="r", encoding="utf-8") as _key:
key_data = _key.read()
if keypair.key_data != key_data:
dirty = True
keypair.key_data = key_data
# Validate that cert and key are actually PEM and valid
serializer = CertificateKeyPairSerializer(instance=keypair)
try:
serializer.validate_certificate_data(keypair.certificate_data)
if keypair.key_data != "":
serializer.validate_certificate_data(keypair.key_data)
except ValidationError as exc:
self.stderr.write(exc)
sys_exit(1)
if dirty:
keypair.save()
def add_arguments(self, parser):
parser.add_argument("--certificate", type=str, required=True)
parser.add_argument("--private-key", type=str, required=False)
parser.add_argument("--name", type=str, required=True)

View File

@ -6,12 +6,7 @@ from uuid import uuid4
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ec import (
EllipticCurvePrivateKey,
EllipticCurvePublicKey,
)
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES, PUBLIC_KEY_TYPES
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import Certificate, load_pem_x509_certificate
from django.db import models
@ -42,8 +37,8 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
)
_cert: Optional[Certificate] = None
_private_key: Optional[RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey] = None
_public_key: Optional[RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey] = None
_private_key: Optional[PRIVATE_KEY_TYPES] = None
_public_key: Optional[PUBLIC_KEY_TYPES] = None
@property
def serializer(self) -> Serializer:
@ -61,7 +56,7 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
return self._cert
@property
def public_key(self) -> Optional[RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey]:
def public_key(self) -> Optional[PUBLIC_KEY_TYPES]:
"""Get public key of the private key"""
if not self._public_key:
self._public_key = self.private_key.public_key()
@ -70,7 +65,7 @@ class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
@property
def private_key(
self,
) -> Optional[RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey]:
) -> Optional[PRIVATE_KEY_TYPES]:
"""Get python cryptography PrivateKey instance"""
if not self._private_key and self.key_data != "":
try:

View File

@ -85,16 +85,18 @@ class NotificationTransportViewSet(UsedByMixin, ModelViewSet):
"""Send example notification using selected transport. Requires
Modify permissions."""
transport: NotificationTransport = self.get_object()
event = Event.new(
action="notification_test",
user=get_user(request.user),
app=self.__class__.__module__,
context={"foo": "bar"},
)
event.save()
notification = Notification(
severity=NotificationSeverity.NOTICE,
body=f"Test Notification from transport {transport.name}",
user=request.user,
event=Event(
action="Test",
user=get_user(request.user),
app=self.__class__.__module__,
context={"foo": "bar"},
),
event=event,
)
try:
response = NotificationTransportTestSerializer(

View File

@ -1,7 +1,7 @@
"""authentik events app"""
from prometheus_client import Gauge
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
GAUGE_TASKS = Gauge(
"authentik_system_tasks",

View File

@ -19,7 +19,7 @@ from authentik.flows.models import FlowToken
from authentik.lib.sentry import before_send
from authentik.lib.utils.errors import exception_to_string
IGNORED_MODELS = [
IGNORED_MODELS = (
Event,
Notification,
UserObjectPermission,
@ -27,12 +27,14 @@ IGNORED_MODELS = [
StaticToken,
Session,
FlowToken,
]
if settings.DEBUG:
from silk.models import Request, Response, SQLQuery
)
IGNORED_MODELS += [Request, Response, SQLQuery]
IGNORED_MODELS = tuple(IGNORED_MODELS)
def should_log_model(model: Model) -> bool:
"""Return true if operation on `model` should be logged"""
if model.__module__.startswith("silk"):
return False
return not isinstance(model, IGNORED_MODELS)
class AuditMiddleware:
@ -109,7 +111,7 @@ class AuditMiddleware:
user: User, request: HttpRequest, sender, instance: Model, created: bool, **_
):
"""Signal handler for all object's post_save"""
if isinstance(instance, IGNORED_MODELS):
if not should_log_model(instance):
return
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
@ -119,7 +121,7 @@ class AuditMiddleware:
# pylint: disable=unused-argument
def pre_delete_handler(user: User, request: HttpRequest, sender, instance: Model, **_):
"""Signal handler for all object's pre_delete"""
if isinstance(instance, IGNORED_MODELS): # pragma: no cover
if not should_log_model(instance): # pragma: no cover
return
EventNewThread(

View File

@ -28,126 +28,6 @@ def convert_user_to_json(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
event.save()
def notify_configuration_error(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("authentik_core", "Group")
PolicyBinding = apps.get_model("authentik_policies", "PolicyBinding")
EventMatcherPolicy = apps.get_model("authentik_policies_event_matcher", "EventMatcherPolicy")
NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = (
Group.objects.using(db_alias).filter(name="authentik Admins", is_superuser=True).first()
)
policy, _ = EventMatcherPolicy.objects.using(db_alias).update_or_create(
name="default-match-configuration-error",
defaults={"action": EventAction.CONFIGURATION_ERROR},
)
trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-configuration-error",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
)
trigger.transports.set(
NotificationTransport.objects.using(db_alias).filter(name="default-email-transport")
)
trigger.save()
PolicyBinding.objects.using(db_alias).update_or_create(
target=trigger,
policy=policy,
defaults={
"order": 0,
},
)
def notify_update(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("authentik_core", "Group")
PolicyBinding = apps.get_model("authentik_policies", "PolicyBinding")
EventMatcherPolicy = apps.get_model("authentik_policies_event_matcher", "EventMatcherPolicy")
NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = (
Group.objects.using(db_alias).filter(name="authentik Admins", is_superuser=True).first()
)
policy, _ = EventMatcherPolicy.objects.using(db_alias).update_or_create(
name="default-match-update",
defaults={"action": EventAction.UPDATE_AVAILABLE},
)
trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-update",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
)
trigger.transports.set(
NotificationTransport.objects.using(db_alias).filter(name="default-email-transport")
)
trigger.save()
PolicyBinding.objects.using(db_alias).update_or_create(
target=trigger,
policy=policy,
defaults={
"order": 0,
},
)
def notify_exception(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Group = apps.get_model("authentik_core", "Group")
PolicyBinding = apps.get_model("authentik_policies", "PolicyBinding")
EventMatcherPolicy = apps.get_model("authentik_policies_event_matcher", "EventMatcherPolicy")
NotificationRule = apps.get_model("authentik_events", "NotificationRule")
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
admin_group = (
Group.objects.using(db_alias).filter(name="authentik Admins", is_superuser=True).first()
)
policy_policy_exc, _ = EventMatcherPolicy.objects.using(db_alias).update_or_create(
name="default-match-policy-exception",
defaults={"action": EventAction.POLICY_EXCEPTION},
)
policy_pm_exc, _ = EventMatcherPolicy.objects.using(db_alias).update_or_create(
name="default-match-property-mapping-exception",
defaults={"action": EventAction.PROPERTY_MAPPING_EXCEPTION},
)
trigger, _ = NotificationRule.objects.using(db_alias).update_or_create(
name="default-notify-exception",
defaults={"group": admin_group, "severity": NotificationSeverity.ALERT},
)
trigger.transports.set(
NotificationTransport.objects.using(db_alias).filter(name="default-email-transport")
)
trigger.save()
PolicyBinding.objects.using(db_alias).update_or_create(
target=trigger,
policy=policy_policy_exc,
defaults={
"order": 0,
},
)
PolicyBinding.objects.using(db_alias).update_or_create(
target=trigger,
policy=policy_pm_exc,
defaults={
"order": 1,
},
)
def transport_email_global(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
NotificationTransport = apps.get_model("authentik_events", "NotificationTransport")
NotificationTransport.objects.using(db_alias).update_or_create(
name="default-email-transport",
defaults={"mode": TransportMode.EMAIL},
)
def token_view_to_secret_view(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.events.models import EventAction
@ -432,18 +312,6 @@ class Migration(migrations.Migration):
"verbose_name_plural": "Notifications",
},
),
migrations.RunPython(
code=transport_email_global,
),
migrations.RunPython(
code=notify_configuration_error,
),
migrations.RunPython(
code=notify_update,
),
migrations.RunPython(
code=notify_exception,
),
migrations.AddField(
model_name="notificationtransport",
name="send_once",

View File

@ -22,14 +22,20 @@ from django.utils.translation import gettext as _
from requests import RequestException
from structlog.stdlib import get_logger
from authentik import __version__
from authentik import get_full_version
from authentik.core.middleware import (
SESSION_KEY_IMPERSONATE_ORIGINAL_USER,
SESSION_KEY_IMPERSONATE_USER,
)
from authentik.core.models import ExpiringModel, Group, PropertyMapping, User
from authentik.events.geo import GEOIP_READER
from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
from authentik.events.utils import (
cleanse_dict,
get_user,
model_to_dict,
sanitize_dict,
sanitize_item,
)
from authentik.lib.models import DomainlessURLValidator, SerializerModel
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip, get_http_session
@ -355,10 +361,12 @@ class NotificationTransport(SerializerModel):
"user_username": notification.user.username,
}
if self.webhook_mapping:
default_body = self.webhook_mapping.evaluate(
user=notification.user,
request=None,
notification=notification,
default_body = sanitize_item(
self.webhook_mapping.evaluate(
user=notification.user,
request=None,
notification=notification,
)
)
try:
response = get_http_session().post(
@ -406,7 +414,7 @@ class NotificationTransport(SerializerModel):
"title": notification.body,
"color": "#fd4b2d",
"fields": fields,
"footer": f"authentik v{__version__}",
"footer": f"authentik {get_full_version()}",
}
],
}

View File

@ -134,26 +134,31 @@ class MonitoredTask(Task):
# pylint: disable=too-many-arguments
def after_return(self, status, retval, task_id, args: list[Any], kwargs: dict[str, Any], einfo):
if self._result:
if not self._result.uid:
self._result.uid = self._uid
if self.save_on_success:
TaskInfo(
task_name=self.__name__,
task_description=self.__doc__,
start_timestamp=self.start,
finish_timestamp=default_timer(),
finish_time=datetime.now(),
result=self._result,
task_call_module=self.__module__,
task_call_func=self.__name__,
task_call_args=args,
task_call_kwargs=kwargs,
).save(self.result_timeout_hours)
return super().after_return(status, retval, task_id, args, kwargs, einfo=einfo)
super().after_return(status, retval, task_id, args, kwargs, einfo=einfo)
if not self._result:
return
if not self._result.uid:
self._result.uid = self._uid
info = TaskInfo(
task_name=self.__name__,
task_description=self.__doc__,
start_timestamp=self.start,
finish_timestamp=default_timer(),
finish_time=datetime.now(),
result=self._result,
task_call_module=self.__module__,
task_call_func=self.__name__,
task_call_args=args,
task_call_kwargs=kwargs,
)
if self._result.status == TaskResultStatus.SUCCESSFUL and not self.save_on_success:
info.delete()
return
info.save(self.result_timeout_hours)
# pylint: disable=too-many-arguments
def on_failure(self, exc, task_id, args, kwargs, einfo):
super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
if not self._result:
self._result = TaskResult(status=TaskResultStatus.ERROR, messages=[str(exc)])
if not self._result.uid:
@ -174,7 +179,6 @@ class MonitoredTask(Task):
EventAction.SYSTEM_TASK_EXCEPTION,
message=(f"Task {self.__name__} encountered an error: {exception_to_string(exc)}"),
).save()
return super().on_failure(exc, task_id, args, kwargs, einfo=einfo)
def run(self, *args, **kwargs):
raise NotImplementedError

View File

@ -31,8 +31,8 @@ class TestEventsNotifications(TestCase):
def test_trigger_empty(self):
"""Test trigger without any policies attached"""
transport = NotificationTransport.objects.create(name="transport")
trigger = NotificationRule.objects.create(name="trigger", group=self.group)
transport = NotificationTransport.objects.create(name=generate_id())
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)
trigger.transports.add(transport)
trigger.save()
@ -43,8 +43,8 @@ class TestEventsNotifications(TestCase):
def test_trigger_single(self):
"""Test simple transport triggering"""
transport = NotificationTransport.objects.create(name="transport")
trigger = NotificationRule.objects.create(name="trigger", group=self.group)
transport = NotificationTransport.objects.create(name=generate_id())
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)
trigger.transports.add(transport)
trigger.save()
matcher = EventMatcherPolicy.objects.create(
@ -59,7 +59,7 @@ class TestEventsNotifications(TestCase):
def test_trigger_no_group(self):
"""Test trigger without group"""
trigger = NotificationRule.objects.create(name="trigger")
trigger = NotificationRule.objects.create(name=generate_id())
matcher = EventMatcherPolicy.objects.create(
name="matcher", action=EventAction.CUSTOM_PREFIX
)
@ -72,9 +72,9 @@ class TestEventsNotifications(TestCase):
def test_policy_error_recursive(self):
"""Test Policy error which would cause recursion"""
transport = NotificationTransport.objects.create(name="transport")
transport = NotificationTransport.objects.create(name=generate_id())
NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)
trigger.transports.add(transport)
trigger.save()
matcher = EventMatcherPolicy.objects.create(
@ -95,9 +95,9 @@ class TestEventsNotifications(TestCase):
self.group.users.add(user2)
self.group.save()
transport = NotificationTransport.objects.create(name="transport", send_once=True)
transport = NotificationTransport.objects.create(name=generate_id(), send_once=True)
NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)
trigger.transports.add(transport)
trigger.save()
matcher = EventMatcherPolicy.objects.create(
@ -118,10 +118,10 @@ class TestEventsNotifications(TestCase):
)
transport = NotificationTransport.objects.create(
name="transport", webhook_mapping=mapping, mode=TransportMode.LOCAL
name=generate_id(), webhook_mapping=mapping, mode=TransportMode.LOCAL
)
NotificationRule.objects.filter(name__startswith="default").delete()
trigger = NotificationRule.objects.create(name="trigger", group=self.group)
trigger = NotificationRule.objects.create(name=generate_id(), group=self.group)
trigger.transports.add(transport)
matcher = EventMatcherPolicy.objects.create(
name="matcher", action=EventAction.CUSTOM_PREFIX

View File

@ -0,0 +1,131 @@
"""transport tests"""
from unittest.mock import PropertyMock, patch
from django.core import mail
from django.core.mail.backends.locmem import EmailBackend
from django.test import TestCase
from requests_mock import Mocker
from authentik import get_full_version
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import (
Event,
Notification,
NotificationSeverity,
NotificationTransport,
NotificationWebhookMapping,
TransportMode,
)
from authentik.lib.generators import generate_id
class TestEventTransports(TestCase):
"""Test Event Transports"""
def setUp(self) -> None:
self.user = create_test_admin_user()
self.event = Event.new("foo", "testing", foo="bar,").set_user(self.user)
self.event.save()
self.notification = Notification.objects.create(
severity=NotificationSeverity.ALERT,
body="foo",
event=self.event,
user=self.user,
)
def test_transport_webhook(self):
"""Test webhook transport"""
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.WEBHOOK,
webhook_url="http://localhost:1234/test",
)
with Mocker() as mocker:
mocker.post("http://localhost:1234/test")
transport.send(self.notification)
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
self.assertJSONEqual(
mocker.request_history[0].body.decode(),
{
"body": "foo",
"severity": "alert",
"user_email": self.user.email,
"user_username": self.user.username,
},
)
def test_transport_webhook_mapping(self):
"""Test webhook transport with custom mapping"""
mapping = NotificationWebhookMapping.objects.create(
name=generate_id(), expression="return request.user"
)
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.WEBHOOK,
webhook_url="http://localhost:1234/test",
webhook_mapping=mapping,
)
with Mocker() as mocker:
mocker.post("http://localhost:1234/test")
transport.send(self.notification)
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
self.assertJSONEqual(
mocker.request_history[0].body.decode(),
{"email": self.user.email, "pk": self.user.pk, "username": self.user.username},
)
def test_transport_webhook_slack(self):
"""Test webhook transport (slack)"""
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.WEBHOOK_SLACK,
webhook_url="http://localhost:1234/test",
)
with Mocker() as mocker:
mocker.post("http://localhost:1234/test")
transport.send(self.notification)
self.assertEqual(mocker.call_count, 1)
self.assertEqual(mocker.request_history[0].method, "POST")
self.assertJSONEqual(
mocker.request_history[0].body.decode(),
{
"username": "authentik",
"icon_url": "https://goauthentik.io/img/icon.png",
"attachments": [
{
"author_name": "authentik",
"author_link": "https://goauthentik.io",
"author_icon": "https://goauthentik.io/img/icon.png",
"title": "custom_foo",
"color": "#fd4b2d",
"fields": [
{"title": "Severity", "value": "alert", "short": True},
{
"title": "Dispatched for user",
"value": self.user.username,
"short": True,
},
{"title": "foo", "value": "bar,"},
],
"footer": f"authentik {get_full_version()}",
}
],
},
)
def test_transport_email(self):
"""Test email transport"""
transport: NotificationTransport = NotificationTransport.objects.create(
name=generate_id(),
mode=TransportMode.EMAIL,
)
with patch(
"authentik.stages.email.models.EmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
transport.send(self.notification)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik Notification: custom_foo")
self.assertIn(self.notification.body, mail.outbox[0].alternatives[0][0])

View File

@ -1,21 +1,17 @@
"""Flow API Views"""
from dataclasses import dataclass
from django.core.cache import cache
from django.db.models import Model
from django.http import HttpResponse
from django.http.response import HttpResponseBadRequest
from django.urls import reverse
from django.utils.translation import gettext as _
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import ReadOnlyField
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import CharField, ModelSerializer, Serializer, SerializerMethodField
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger
@ -29,6 +25,7 @@ from authentik.core.api.utils import (
FileUploadSerializer,
LinkSerializer,
)
from authentik.flows.api.flows_diagram import FlowDiagram, FlowDiagramSerializer
from authentik.flows.exceptions import FlowNonApplicableException
from authentik.flows.models import Flow
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
@ -80,30 +77,6 @@ class FlowSerializer(ModelSerializer):
}
class FlowDiagramSerializer(Serializer):
"""response of the flow's diagram action"""
diagram = CharField(read_only=True)
def create(self, validated_data: dict) -> Model:
raise NotImplementedError
def update(self, instance: Model, validated_data: dict) -> Model:
raise NotImplementedError
@dataclass
class DiagramElement:
"""Single element used in a diagram"""
identifier: str
type: str
rest: str
def __str__(self) -> str:
return f"{self.identifier}=>{self.type}: {self.rest}"
class FlowViewSet(UsedByMixin, ModelViewSet):
"""Flow Viewset"""
@ -208,84 +181,9 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
# pylint: disable=unused-argument
def diagram(self, request: Request, slug: str) -> Response:
"""Return diagram for flow with slug `slug`, in the format used by flowchart.js"""
flow = self.get_object()
header = [
DiagramElement("st", "start", "Start"),
]
body: list[DiagramElement] = []
footer = []
# Collect all elements we need
# First, policies bound to the flow itself
for p_index, policy_binding in enumerate(
get_objects_for_user(request.user, "authentik_policies.view_policybinding")
.filter(target=flow)
.exclude(policy__isnull=True)
.order_by("order")
):
body.append(
DiagramElement(
f"flow_policy_{p_index}",
"condition",
_("Policy (%(type)s)" % {"type": policy_binding.policy._meta.verbose_name})
+ "\n"
+ policy_binding.policy.name,
)
)
# Collect all stages
for s_index, stage_binding in enumerate(
get_objects_for_user(request.user, "authentik_flows.view_flowstagebinding")
.filter(target=flow)
.order_by("order")
):
# First all policies bound to stages since they execute before stages
for p_index, policy_binding in enumerate(
get_objects_for_user(request.user, "authentik_policies.view_policybinding")
.filter(target=stage_binding)
.exclude(policy__isnull=True)
.order_by("order")
):
body.append(
DiagramElement(
f"stage_{s_index}_policy_{p_index}",
"condition",
_("Policy (%(type)s)" % {"type": policy_binding.policy._meta.verbose_name})
+ "\n"
+ policy_binding.policy.name,
)
)
body.append(
DiagramElement(
f"stage_{s_index}",
"operation",
_("Stage (%(type)s)" % {"type": stage_binding.stage._meta.verbose_name})
+ "\n"
+ stage_binding.stage.name,
)
)
# If the 2nd last element is a policy, we need to have an item to point to
# for a negative case
body.append(
DiagramElement("e", "end", "End|future"),
)
if len(body) == 1:
footer.append("st(right)->e")
else:
# Actual diagram flow
footer.append(f"st(right)->{body[0].identifier}")
for index in range(len(body) - 1):
element: DiagramElement = body[index]
if element.type == "condition":
# Policy passes, link policy yes to next stage
footer.append(f"{element.identifier}(yes, right)->{body[index + 1].identifier}")
# Policy doesn't pass, go to stage after next stage
no_element = body[index + 1]
if no_element.type != "end":
no_element = body[index + 2]
footer.append(f"{element.identifier}(no, bottom)->{no_element.identifier}")
elif element.type == "operation":
footer.append(f"{element.identifier}(bottom)->{body[index + 1].identifier}")
diagram = "\n".join([str(x) for x in header + body + footer])
return Response({"diagram": diagram})
diagram = FlowDiagram(self.get_object(), request.user)
output = diagram.build()
return Response({"diagram": output})
@permission_required("authentik_flows.change_flow")
@extend_schema(

View File

@ -0,0 +1,206 @@
"""Flows Diagram API"""
from dataclasses import dataclass, field
from typing import Optional
from django.utils.translation import gettext as _
from guardian.shortcuts import get_objects_for_user
from rest_framework.serializers import CharField
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import User
from authentik.flows.models import Flow, FlowStageBinding
@dataclass
class DiagramElement:
"""Single element used in a diagram"""
identifier: str
description: str
action: Optional[str] = None
source: Optional[list["DiagramElement"]] = None
style: list[str] = field(default_factory=lambda: ["[", "]"])
def __str__(self) -> str:
element = f'{self.identifier}{self.style[0]}"{self.description}"{self.style[1]}'
if self.action is not None:
if self.action != "":
element = f"--{self.action}--> {element}"
else:
element = f"--> {element}"
if self.source:
source_element = []
for source in self.source:
source_element.append(f"{source.identifier} {element}")
return "\n".join(source_element)
return element
class FlowDiagramSerializer(PassiveSerializer):
"""response of the flow's diagram action"""
diagram = CharField(read_only=True)
class FlowDiagram:
"""Generate flow chart fow a flow"""
flow: Flow
user: User
def __init__(self, flow: Flow, user: User) -> None:
self.flow = flow
self.user = user
def get_flow_policies(self, parent_elements: list[DiagramElement]) -> list[DiagramElement]:
"""Collect all policies bound to the flow"""
elements = []
for p_index, policy_binding in enumerate(
get_objects_for_user(self.user, "authentik_policies.view_policybinding")
.filter(target=self.flow)
.exclude(policy__isnull=True)
.order_by("order")
):
element = DiagramElement(
f"flow_policy_{p_index}",
_("Policy (%(type)s)" % {"type": policy_binding.policy._meta.verbose_name})
+ "\n"
+ policy_binding.policy.name,
_("Binding %(order)d" % {"order": policy_binding.order}),
parent_elements,
style=["{{", "}}"],
)
elements.append(element)
return elements
def get_stage_policies(
self,
stage_index: int,
stage_binding: FlowStageBinding,
parent_elements: list[DiagramElement],
) -> list[DiagramElement]:
"""First all policies bound to stages since they execute before stages"""
elements = []
for p_index, policy_binding in enumerate(
get_objects_for_user(self.user, "authentik_policies.view_policybinding")
.filter(target=stage_binding)
.exclude(policy__isnull=True)
.order_by("order")
):
element = DiagramElement(
f"stage_{stage_index}_policy_{p_index}",
_("Policy (%(type)s)" % {"type": policy_binding.policy._meta.verbose_name})
+ "\n"
+ policy_binding.policy.name,
"",
parent_elements,
style=["{{", "}}"],
)
elements.append(element)
return elements
def get_stages(self, parent_elements: list[DiagramElement]) -> list[str | DiagramElement]:
"""Collect all stages"""
elements = []
stages = []
for s_index, stage_binding in enumerate(
get_objects_for_user(self.user, "authentik_flows.view_flowstagebinding")
.filter(target=self.flow)
.order_by("order")
):
stage_policies = self.get_stage_policies(s_index, stage_binding, parent_elements)
elements.extend(stage_policies)
action = ""
if len(stage_policies) > 0:
action = _("Policy passed")
element = DiagramElement(
f"stage_{s_index}",
_("Stage (%(type)s)" % {"type": stage_binding.stage._meta.verbose_name})
+ "\n"
+ stage_binding.stage.name,
action,
stage_policies,
style=["([", "])"],
)
stages.append(element)
parent_elements = [element]
# This adds connections for policy denies, but retroactively, as we can't really
# look ahead
# Check if we have a stage behind us and if it has any sources
if s_index > 0:
last_stage: DiagramElement = stages[s_index - 1]
if last_stage.source and len(last_stage.source) > 0:
# If it has any sources, add a connection from each of that stage's sources
# to this stage
for source in last_stage.source:
elements.append(
DiagramElement(
element.identifier,
element.description,
_("Policy denied"),
[source],
style=element.style,
)
)
if len(stages) > 0:
elements.append(
DiagramElement(
"done",
_("End of the flow"),
"",
[stages[-1]],
style=["[[", "]]"],
),
)
return stages + elements
def build(self) -> str:
"""Build flowchart"""
all_elements = [
"graph TD",
]
pre_flow_policies_element = DiagramElement(
"flow_pre", _("Pre-flow policies"), style=["[[", "]]"]
)
flow_policies = self.get_flow_policies([pre_flow_policies_element])
if len(flow_policies) > 0:
all_elements.append(pre_flow_policies_element)
all_elements.extend(flow_policies)
all_elements.append(
DiagramElement(
"done",
_("End of the flow"),
_("Policy denied"),
flow_policies,
)
)
flow_element = DiagramElement(
"flow_start",
_("Flow") + "\n" + self.flow.name,
"" if len(flow_policies) > 0 else None,
source=flow_policies,
style=["[[", "]]"],
)
all_elements.append(flow_element)
stages = self.get_stages([flow_element])
all_elements.extend(stages)
if len(stages) < 1:
all_elements.append(
DiagramElement(
"done",
_("End of the flow"),
"",
[flow_element],
style=["[[", "]]"],
),
)
return "\n".join([str(x) for x in all_elements])

View File

@ -1,7 +1,7 @@
"""authentik flows app config"""
from prometheus_client import Gauge, Histogram
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.utils.reflection import all_subclasses
GAUGE_FLOWS_CACHED = Gauge(

View File

@ -1,14 +1,14 @@
"""Challenge helpers"""
from dataclasses import asdict, is_dataclass
from enum import Enum
from traceback import format_tb
from typing import TYPE_CHECKING, Optional, TypedDict
from uuid import UUID
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.http import JsonResponse
from rest_framework.fields import ChoiceField, DictField
from rest_framework.serializers import CharField
from rest_framework.fields import CharField, ChoiceField, DictField
from authentik.core.api.utils import PassiveSerializer
@ -90,6 +90,34 @@ class WithUserInfoChallenge(Challenge):
pending_user_avatar = CharField()
class FlowErrorChallenge(WithUserInfoChallenge):
"""Challenge class when an unhandled error occurs during a stage. Normal users
are shown an error message, superusers are shown a full stacktrace."""
component = CharField(default="xak-flow-error")
request_id = CharField()
error = CharField(required=False)
traceback = CharField(required=False)
def __init__(self, *args, **kwargs):
request = kwargs.pop("request", None)
error = kwargs.pop("error", None)
super().__init__(*args, **kwargs)
if not request or not error:
return
self.request_id = request.request_id
from authentik.core.models import USER_ATTRIBUTE_DEBUG
if request.user and request.user.is_authenticated:
if request.user.is_superuser or request.user.group_attributes(request).get(
USER_ATTRIBUTE_DEBUG, False
):
self.error = error
self.traceback = "".join(format_tb(self.error.__traceback__))
class AccessDeniedChallenge(WithUserInfoChallenge):
"""Challenge when a flow's active stage calls `stage_invalid()`."""

View File

@ -32,7 +32,7 @@ class FlowPlanProcess(PROCESS_CLASS): # pragma: no cover
def run(self):
"""Execute 1000 flow plans"""
print(f"Proc {self.index} Running")
LOGGER.info(f"Proc {self.index} Running")
def test_inner():
planner = FlowPlanner(self.flow)

View File

@ -19,25 +19,6 @@ def update_flow_designation(apps: Apps, schema_editor: BaseDatabaseSchemaEditor)
flow.save()
# First stage for default-source-enrollment flow (prompt stage)
# needs to have its policy re-evaluated
def update_default_source_enrollment_flow_binding(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
):
Flow = apps.get_model("authentik_flows", "Flow")
FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding")
db_alias = schema_editor.connection.alias
flows = Flow.objects.using(db_alias).filter(slug="default-source-enrollment")
if not flows.exists():
return
flow = flows.first()
binding = FlowStageBinding.objects.get(target=flow, order=0)
binding.re_evaluate_policies = True
binding.save()
class Migration(migrations.Migration):
replaces = [
@ -101,9 +82,6 @@ class Migration(migrations.Migration):
help_text="When this option is enabled, the planner will re-evaluate policies bound to this binding.",
),
),
migrations.RunPython(
code=update_default_source_enrollment_flow_binding,
),
migrations.AlterField(
model_name="flowstagebinding",
name="re_evaluate_policies",

View File

@ -1,22 +0,0 @@
{% load i18n %}
<style>
.ak-exception {
font-family: monospace;
overflow-x: scroll;
}
</style>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
{% trans 'Whoops!' %}
</h1>
</header>
<div class="pf-c-login__main-body">
<h3>
{% trans 'Something went wrong! Please try again later.' %}
</h3>
{% if debug %}
<pre class="ak-exception">{{ tb }}{{ error }}</pre>
{% endif %}
</div>

View File

@ -9,22 +9,20 @@ from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
from authentik.stages.dummy.models import DummyStage
DIAGRAM_EXPECTED = """st=>start: Start
stage_0=>operation: Stage (Dummy Stage)
dummy1
stage_1_policy_0=>condition: Policy (Dummy Policy)
test
stage_1=>operation: Stage (Dummy Stage)
dummy2
e=>end: End|future
st(right)->stage_0
stage_0(bottom)->stage_1_policy_0
stage_1_policy_0(yes, right)->stage_1
stage_1_policy_0(no, bottom)->e
stage_1(bottom)->e"""
DIAGRAM_SHORT_EXPECTED = """st=>start: Start
e=>end: End|future
st(right)->e"""
DIAGRAM_EXPECTED = """graph TD
flow_start[["Flow
test-default-context"]]
--> stage_0(["Stage (Dummy Stage)
dummy1"])
stage_1_policy_0 --Policy passed--> stage_1(["Stage (Dummy Stage)
dummy2"])
stage_0 --> stage_1_policy_0{{"Policy (Dummy Policy)
dummy2-policy"}}
stage_1 --> done[["End of the flow"]]"""
DIAGRAM_SHORT_EXPECTED = """graph TD
flow_start[["Flow
test-default-context"]]
flow_start --> done[["End of the flow"]]"""
class TestFlowsAPI(APITestCase):
@ -55,7 +53,9 @@ class TestFlowsAPI(APITestCase):
slug="test-default-context",
designation=FlowDesignation.AUTHENTICATION,
)
false_policy = DummyPolicy.objects.create(name="test", result=False, wait_min=1, wait_max=2)
false_policy = DummyPolicy.objects.create(
name="dummy2-policy", result=False, wait_min=1, wait_max=2
)
FlowStageBinding.objects.create(
target=flow, stage=DummyStage.objects.create(name="dummy1"), order=0

View File

@ -6,10 +6,9 @@ from django.test.client import RequestFactory
from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
from authentik.lib.generators import generate_id
from authentik.flows.models import FlowDesignation, FlowStageBinding, InvalidResponseAction
from authentik.stages.dummy.models import DummyStage
from authentik.stages.identification.models import IdentificationStage, UserFields
@ -24,11 +23,7 @@ class TestFlowInspector(APITestCase):
def test(self):
"""test inspector"""
flow = Flow.objects.create(
name=generate_id(),
slug=generate_id(),
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
# Stage 1 is an identification stage
ident_stage = IdentificationStage.objects.create(
@ -55,7 +50,7 @@ class TestFlowInspector(APITestCase):
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
"title": flow.title,
"layout": "stacked",
},
"type": ChallengeTypes.NATIVE.value,

View File

@ -8,9 +8,10 @@ from django.urls import reverse
from guardian.shortcuts import get_anonymous_user
from authentik.core.models import User
from authentik.core.tests.utils import create_test_flow
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
from authentik.flows.markers import ReevaluateMarker, StageMarker
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.flows.models import FlowDesignation, FlowStageBinding
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
from authentik.lib.tests.utils import dummy_get_response
from authentik.policies.dummy.models import DummyPolicy
@ -32,11 +33,7 @@ class TestFlowPlanner(TestCase):
def test_empty_plan(self):
"""Test that empty plan raises exception"""
flow = Flow.objects.create(
name="test-empty",
slug="test-empty",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow()
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
@ -52,11 +49,7 @@ class TestFlowPlanner(TestCase):
)
def test_non_applicable_plan(self):
"""Test that empty plan raises exception"""
flow = Flow.objects.create(
name="test-empty",
slug="test-empty",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow()
request = self.request_factory.get(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
)
@ -69,11 +62,7 @@ class TestFlowPlanner(TestCase):
@patch("authentik.flows.planner.cache", CACHE_MOCK)
def test_planner_cache(self):
"""Test planner cache"""
flow = Flow.objects.create(
name="test-cache",
slug="test-cache",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow(FlowDesignation.AUTHENTICATION)
FlowStageBinding.objects.create(
target=flow, stage=DummyStage.objects.create(name="dummy"), order=0
)
@ -92,11 +81,7 @@ class TestFlowPlanner(TestCase):
def test_planner_default_context(self):
"""Test planner with default_context"""
flow = Flow.objects.create(
name="test-default-context",
slug="test-default-context",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow()
FlowStageBinding.objects.create(
target=flow, stage=DummyStage.objects.create(name="dummy"), order=0
)
@ -113,11 +98,7 @@ class TestFlowPlanner(TestCase):
def test_planner_marker_reevaluate(self):
"""Test that the planner creates the proper marker"""
flow = Flow.objects.create(
name="test-default-context",
slug="test-default-context",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow()
FlowStageBinding.objects.create(
target=flow,
@ -138,11 +119,7 @@ class TestFlowPlanner(TestCase):
def test_planner_reevaluate_actual(self):
"""Test planner with re-evaluate"""
flow = Flow.objects.create(
name="test-default-context",
slug="test-default-context",
designation=FlowDesignation.AUTHENTICATION,
)
flow = create_test_flow()
false_policy = DummyPolicy.objects.create(result=False, wait_min=1, wait_max=2)
binding = FlowStageBinding.objects.create(

View File

@ -1,7 +1,6 @@
"""authentik multi-stage authentication engine"""
from copy import deepcopy
from traceback import format_tb
from typing import Any, Optional
from typing import Optional
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
@ -23,12 +22,12 @@ from sentry_sdk.api import set_tag
from sentry_sdk.hub import Hub
from structlog.stdlib import BoundLogger, get_logger
from authentik.core.models import USER_ATTRIBUTE_DEBUG
from authentik.events.models import Event, EventAction, cleanse_dict
from authentik.flows.challenge import (
Challenge,
ChallengeResponse,
ChallengeTypes,
FlowErrorChallenge,
HttpChallengeResponse,
RedirectChallenge,
ShellChallenge,
@ -153,6 +152,7 @@ class FlowExecutorView(APIView):
token: Optional[FlowToken] = FlowToken.filter_not_expired(key=key).first()
if not token:
return None
plan = None
try:
plan = token.plan
except (AttributeError, EOFError, ImportError, IndexError) as exc:
@ -253,7 +253,9 @@ class FlowExecutorView(APIView):
action=EventAction.SYSTEM_EXCEPTION,
message=exception_to_string(exc),
).from_http(self.request)
return to_stage_response(self.request, FlowErrorResponse(self.request, exc))
return to_stage_response(
self.request, HttpChallengeResponse(FlowErrorChallenge(self.request, exc))
)
@extend_schema(
responses={
@ -440,30 +442,6 @@ class FlowExecutorView(APIView):
del self.request.session[key]
class FlowErrorResponse(TemplateResponse):
"""Response class when an unhandled error occurs during a stage. Normal users
are shown an error message, superusers are shown a full stacktrace."""
error: Exception
def __init__(self, request: HttpRequest, error: Exception) -> None:
# For some reason pyright complains about keyword argument usage here
# pyright: reportGeneralTypeIssues=false
super().__init__(request=request, template="flows/error.html")
self.error = error
def resolve_context(self, context: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
if not context:
context = {}
context["error"] = self.error
if self._request.user and self._request.user.is_authenticated:
if self._request.user.is_superuser or self._request.user.group_attributes(
self._request
).get(USER_ATTRIBUTE_DEBUG, False):
context["tb"] = "".join(format_tb(self.error.__traceback__))
return context
class CancelView(View):
"""View which canels the currently active plan"""

View File

@ -20,7 +20,7 @@ ENV_PREFIX = "AUTHENTIK"
ENVIRONMENT = os.getenv(f"{ENV_PREFIX}_ENV", "local")
def get_path_from_dict(root: dict, path: str, sep=".", default=None):
def get_path_from_dict(root: dict, path: str, sep=".", default=None) -> Any:
"""Recursively walk through `root`, checking each part of `path` split by `sep`.
If at any point a dict does not exist, return default"""
for comp in path.split(sep):
@ -62,7 +62,7 @@ class ConfigLoader:
self.update_from_file(env_file)
self.update_from_env()
def _log(self, level: str, message: str, **kwargs):
def log(self, level: str, message: str, **kwargs):
"""Custom Log method, we want to ensure ConfigLoader always logs JSON even when
'structlog' or 'logging' hasn't been configured yet."""
output = {
@ -95,7 +95,7 @@ class ConfigLoader:
with open(url.path, "r", encoding="utf8") as _file:
value = _file.read()
except OSError as exc:
self._log("error", f"Failed to read config value from {url.path}: {exc}")
self.log("error", f"Failed to read config value from {url.path}: {exc}")
value = url.query
return value
@ -105,12 +105,12 @@ class ConfigLoader:
with open(path, encoding="utf8") as file:
try:
self.update(self.__config, yaml.safe_load(file))
self._log("debug", "Loaded config", file=path)
self.log("debug", "Loaded config", file=path)
self.loaded_file.append(path)
except yaml.YAMLError as exc:
raise ImproperlyConfigured from exc
except PermissionError as exc:
self._log(
self.log(
"warning",
"Permission denied while reading file",
path=path,
@ -144,7 +144,7 @@ class ConfigLoader:
current_obj[dot_parts[-1]] = value
idx += 1
if idx > 0:
self._log("debug", "Loaded environment variables", count=idx)
self.log("debug", "Loaded environment variables", count=idx)
self.update(self.__config, outer)
@contextmanager
@ -152,8 +152,10 @@ class ConfigLoader:
"""Context manager for unittests to patch a value"""
original_value = self.y(path)
self.y_set(path, value)
yield
self.y_set(path, original_value)
try:
yield
finally:
self.y_set(path, original_value)
@property
def raw(self) -> dict:
@ -178,7 +180,7 @@ class ConfigLoader:
# pyright: reportGeneralTypeIssues=false
if comp not in root:
root[comp] = {}
root = root.get(comp)
root = root.get(comp, {})
root[path_parts[-1]] = value
def y_bool(self, path: str, default=False) -> bool:

View File

@ -36,7 +36,7 @@ error_reporting:
enabled: false
environment: customer
send_pii: false
sample_rate: 0.3
sample_rate: 0.1
# Global email settings
email:
@ -80,3 +80,8 @@ default_token_length: 128
impersonation: true
blueprints_dir: /blueprints
web:
# No default here as it's set dynamically
# workers: 2
threads: 4

View File

@ -1,16 +1,20 @@
"""authentik expression policy evaluator"""
import re
from ipaddress import ip_address, ip_network
from textwrap import indent
from typing import Any, Iterable, Optional
from django.core.exceptions import FieldError
from django_otp import devices_for_user
from rest_framework.serializers import ValidationError
from sentry_sdk.hub import Hub
from sentry_sdk.tracing import Span
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.events.models import Event
from authentik.lib.utils.http import get_http_session
from authentik.policies.types import PolicyRequest
LOGGER = get_logger()
@ -26,7 +30,8 @@ class BaseEvaluator:
# Filename used for exec
_filename: str
def __init__(self):
def __init__(self, filename: Optional[str] = None):
self._filename = filename if filename else "BaseEvaluator"
# update website/docs/expressions/_objects.md
# update website/docs/expressions/_functions.md
self._globals = {
@ -35,11 +40,14 @@ class BaseEvaluator:
"list_flatten": BaseEvaluator.expr_flatten,
"ak_is_group_member": BaseEvaluator.expr_is_group_member,
"ak_user_by": BaseEvaluator.expr_user_by,
"ak_logger": get_logger(),
"ak_user_has_authenticator": BaseEvaluator.expr_func_user_has_authenticator,
"ak_create_event": self.expr_event_create,
"ak_logger": get_logger(self._filename),
"requests": get_http_session(),
"ip_address": ip_address,
"ip_network": ip_network,
}
self._context = {}
self._filename = "BaseEvalautor"
@staticmethod
def expr_flatten(value: list[Any] | Any) -> Optional[Any]:
@ -60,6 +68,11 @@ class BaseEvaluator:
"""Expression Filter to run re.sub"""
return re.sub(regex, repl, value)
@staticmethod
def expr_is_group_member(user: User, **group_filters) -> bool:
"""Check if `user` is member of group with name `group_name`"""
return user.ak_groups.filter(**group_filters).exists()
@staticmethod
def expr_user_by(**filters) -> Optional[User]:
"""Get user by filters"""
@ -72,15 +85,37 @@ class BaseEvaluator:
return None
@staticmethod
def expr_is_group_member(user: User, **group_filters) -> bool:
"""Check if `user` is member of group with name `group_name`"""
return user.ak_groups.filter(**group_filters).exists()
def expr_func_user_has_authenticator(user: User, device_type: Optional[str] = None) -> bool:
"""Check if a user has any authenticator devices, optionally matching *device_type*"""
user_devices = devices_for_user(user)
if device_type:
for device in user_devices:
device_class = device.__class__.__name__.lower().replace("device", "")
if device_class == device_type:
return True
return False
return len(list(user_devices)) > 0
def expr_event_create(self, action: str, **kwargs):
"""Create event with supplied data and try to extract as much relevant data
from the context"""
kwargs["context"] = self._context
event = Event.new(
action,
app=self._filename,
**kwargs,
)
if "request" in self._context and isinstance(PolicyRequest, self._context["request"]):
policy_request: PolicyRequest = self._context["request"]
if policy_request.http_request:
event.from_http(policy_request)
return
event.save()
def wrap_expression(self, expression: str, params: Iterable[str]) -> str:
"""Wrap expression in a function, call it, and save the result as `result`"""
handler_signature = ",".join(params)
full_expression = ""
full_expression += "from ipaddress import ip_address, ip_network\n"
full_expression += f"def handler({handler_signature}):\n"
full_expression += indent(expression, " ")
full_expression += f"\nresult = handler({handler_signature})"

View File

@ -95,7 +95,7 @@ def traces_sampler(sampling_context: dict) -> float:
# Ignore all healthcheck routes
if path.startswith("/-/health") or path.startswith("/-/metrics"):
return 0
return float(CONFIG.y("error_reporting.sample_rate", 0.5))
return float(CONFIG.y("error_reporting.sample_rate", 0.1))
def before_send(event: dict, hint: dict) -> Optional[dict]:

View File

@ -20,9 +20,8 @@ def model_tester_factory(test_model: type[Stage]) -> Callable:
try:
model_class = None
if test_model._meta.abstract:
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
return
model_class = test_model()
self.assertTrue(issubclass(model_class.serializer, BaseSerializer))
except NotImplementedError:
pass

View File

@ -12,5 +12,4 @@ class TestReflectionUtils(TestCase):
def test_path_to_class(self):
"""Test path_to_class"""
self.assertIsNone(path_to_class(None))
self.assertEqual(path_to_class("datetime.datetime"), datetime)

View File

@ -29,10 +29,8 @@ def class_to_path(cls: type) -> str:
return f"{cls.__module__}.{cls.__name__}"
def path_to_class(path: str | None) -> type | None:
def path_to_class(path: str = "") -> type:
"""Import module and return class"""
if not path:
return None
parts = path.split(".")
package = ".".join(parts[:-1])
_class = getattr(import_module(package), parts[-1])

View File

@ -8,7 +8,7 @@ def bad_request_message(
request: HttpRequest,
message: str,
title="Bad Request",
template="error/generic.html",
template="if/error.html",
) -> TemplateResponse:
"""Return generic error page with message, with status code set to 400"""
return TemplateResponse(

View File

@ -2,7 +2,7 @@
from prometheus_client import Gauge
from structlog.stdlib import get_logger
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
LOGGER = get_logger()

View File

@ -5,7 +5,7 @@ from enum import IntEnum
from typing import Any, Optional
from channels.exceptions import DenyConnection
from dacite import from_dict
from dacite.core import from_dict
from dacite.data import Data
from guardian.shortcuts import get_objects_for_user
from structlog.stdlib import BoundLogger, get_logger

View File

@ -2,7 +2,7 @@
from dataclasses import asdict, dataclass, field
from typing import TYPE_CHECKING
from dacite import from_dict
from dacite.core import from_dict
from kubernetes.client import ApiextensionsV1Api, CustomObjectsApi
from authentik.outposts.controllers.base import FIELD_MANAGER

View File

@ -4,7 +4,7 @@ from datetime import datetime
from typing import Iterable, Optional
from uuid import uuid4
from dacite import from_dict
from dacite.core import from_dict
from django.contrib.auth.models import Permission
from django.core.cache import cache
from django.db import IntegrityError, models, transaction
@ -74,7 +74,7 @@ class OutpostConfig:
kubernetes_ingress_secret_name: str = field(default="authentik-outpost-tls")
kubernetes_service_type: str = field(default="ClusterIP")
kubernetes_disabled_components: list[str] = field(default_factory=list)
kubernetes_image_pull_secrets: Optional[list[str]] = field(default_factory=list)
kubernetes_image_pull_secrets: list[str] = field(default_factory=list)
class OutpostModel(Model):

View File

@ -74,10 +74,14 @@ def outpost_service_connection_state(connection_pk: Any):
)
if not connection:
return
cls = None
if isinstance(connection, DockerServiceConnection):
cls = DockerClient
if isinstance(connection, KubernetesServiceConnection):
cls = KubernetesClient
if not cls:
LOGGER.warning("No class found for service connection", connection=connection)
return
try:
with cls(connection) as client:
state = client.fetch_state()

View File

@ -6,7 +6,7 @@ from channels.testing import WebsocketCommunicator
from django.test import TransactionTestCase
from authentik import __version__
from authentik.flows.models import Flow, FlowDesignation
from authentik.core.tests.utils import create_test_flow
from authentik.outposts.channels import WebsocketMessage, WebsocketMessageInstruction
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.proxy.models import ProxyProvider
@ -21,9 +21,7 @@ class TestOutpostWS(TransactionTestCase):
name="test",
internal_host="http://localhost",
external_host="http://localhost",
authorization_flow=Flow.objects.create(
name="foo", slug="foo", designation=FlowDesignation.AUTHORIZATION
),
authorization_flow=create_test_flow(),
)
self.outpost: Outpost = Outpost.objects.create(
name="test",

View File

@ -1,7 +1,7 @@
"""authentik policies app config"""
from prometheus_client import Gauge, Histogram
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
GAUGE_POLICIES_CACHED = Gauge(
"authentik_policies_cached",

View File

@ -1,12 +1,10 @@
"""authentik expression policy evaluator"""
from ipaddress import ip_address, ip_network
from ipaddress import ip_address
from typing import TYPE_CHECKING, Optional
from django.http import HttpRequest
from django_otp import devices_for_user
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.flows.planner import PLAN_CONTEXT_SSO
from authentik.lib.expression.evaluator import BaseEvaluator
from authentik.lib.utils.http import get_client_ip
@ -27,16 +25,14 @@ class PolicyEvaluator(BaseEvaluator):
policy: Optional["ExpressionPolicy"] = None
def __init__(self, policy_name: str):
super().__init__()
def __init__(self, policy_name: Optional[str] = None):
super().__init__(policy_name or "PolicyEvaluator")
self._messages = []
self._context["ak_logger"] = get_logger(policy_name)
# update website/docs/expressions/_objects.md
# update website/docs/expressions/_functions.md
self._context["ak_message"] = self.expr_func_message
self._context["ak_user_has_authenticator"] = self.expr_func_user_has_authenticator
self._context["ak_call_policy"] = self.expr_func_call_policy
self._context["ip_address"] = ip_address
self._context["ip_network"] = ip_network
self._filename = policy_name or "PolicyEvaluator"
def expr_func_message(self, message: str):
"""Wrapper to append to messages list, which is returned with PolicyResult"""
@ -52,19 +48,6 @@ class PolicyEvaluator(BaseEvaluator):
proc = PolicyProcess(PolicyBinding(policy=policy), request=req, connection=None)
return proc.profiling_wrapper()
def expr_func_user_has_authenticator(
self, user: User, device_type: Optional[str] = None
) -> bool:
"""Check if a user has any authenticator devices, optionally matching *device_type*"""
user_devices = devices_for_user(user)
if device_type:
for device in user_devices:
device_class = device.__class__.__name__.lower().replace("device", "")
if device_class == device_type:
return True
return False
return len(list(user_devices)) > 0
def set_policy_request(self, request: PolicyRequest):
"""Update context based on policy request (if http request is given, update that too)"""
# update website/docs/expressions/_objects.md

View File

@ -1,5 +1,5 @@
"""Authentik reputation_policy app config"""
from authentik.blueprints.manager import ManagedAppConfig
from authentik.blueprints.apps import ManagedAppConfig
class AuthentikPolicyReputationConfig(ManagedAppConfig):

View File

@ -1,9 +1,12 @@
"""OAuth errors"""
from typing import Optional
from urllib.parse import quote
from urllib.parse import quote, urlparse
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from authentik.events.models import Event, EventAction
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.views import bad_request_message
from authentik.providers.oauth2.models import GrantTypes
@ -150,6 +153,14 @@ class AuthorizeError(OAuth2Error):
self.grant_type = grant_type
self.state = state
def get_response(self, request: HttpRequest) -> HttpResponse:
"""Wrapper around `self.create_uri()` that checks if the resulting URI is valid
(we might not have self.redirect_uri set), and returns a valid HTTP Response"""
uri = self.create_uri()
if urlparse(uri).scheme != "":
return HttpResponseRedirect(uri)
return bad_request_message(request, self.description, title=self.error)
def create_uri(self) -> str:
"""Get a redirect URI with the error message"""
description = quote(str(self.description))

View File

@ -11,7 +11,7 @@ from urllib.parse import urlparse, urlunparse
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from dacite import from_dict
from dacite.core import from_dict
from django.db import models
from django.http import HttpRequest
from django.utils import dateformat, timezone

View File

@ -1,10 +1,7 @@
"""OAuth provider URLs"""
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.base import RedirectView
from authentik.providers.oauth2.constants import SCOPE_OPENID
from authentik.providers.oauth2.utils import protected_resource_view
from authentik.providers.oauth2.views.authorize import AuthorizationFlowInitView
from authentik.providers.oauth2.views.introspection import TokenIntrospectionView
from authentik.providers.oauth2.views.jwks import JWKSView
@ -19,20 +16,20 @@ urlpatterns = [
AuthorizationFlowInitView.as_view(),
name="authorize",
),
path("token/", csrf_exempt(TokenView.as_view()), name="token"),
path("token/", TokenView.as_view(), name="token"),
path(
"userinfo/",
csrf_exempt(protected_resource_view([SCOPE_OPENID])(UserInfoView.as_view())),
UserInfoView.as_view(),
name="userinfo",
),
path(
"introspect/",
csrf_exempt(TokenIntrospectionView.as_view()),
TokenIntrospectionView.as_view(),
name="token-introspection",
),
path(
"revoke/",
csrf_exempt(TokenRevokeView.as_view()),
TokenRevokeView.as_view(),
name="token-revoke",
),
path(

View File

@ -1,9 +1,6 @@
"""authentik oauth_provider urls"""
from django.urls import include, path
from django.views.decorators.csrf import csrf_exempt
from authentik.providers.oauth2.constants import SCOPE_GITHUB_ORG_READ, SCOPE_GITHUB_USER_EMAIL
from authentik.providers.oauth2.utils import protected_resource_view
from authentik.providers.oauth2.views.authorize import AuthorizationFlowInitView
from authentik.providers.oauth2.views.github import GitHubUserTeamsView, GitHubUserView
from authentik.providers.oauth2.views.token import TokenView
@ -16,19 +13,17 @@ github_urlpatterns = [
),
path(
"login/oauth/access_token",
csrf_exempt(TokenView.as_view()),
TokenView.as_view(),
name="github-access-token",
),
path(
"user",
csrf_exempt(protected_resource_view([SCOPE_GITHUB_USER_EMAIL])(GitHubUserView.as_view())),
GitHubUserView.as_view(),
name="github-user",
),
path(
"user/teams",
csrf_exempt(
protected_resource_view([SCOPE_GITHUB_ORG_READ])(GitHubUserTeamsView.as_view())
),
GitHubUserTeamsView.as_view(),
name="github-user-teams",
),
]

View File

@ -8,7 +8,7 @@ from urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit
from uuid import uuid4
from django.http import HttpRequest, HttpResponse
from django.http.response import Http404, HttpResponseBadRequest, HttpResponseRedirect
from django.http.response import Http404, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.translation import gettext as _
@ -73,7 +73,7 @@ ALLOWED_PROMPT_PARAMS = {PROMPT_NONE, PROMPT_CONSENT, PROMPT_LOGIN}
@dataclass
# pylint: disable=too-many-instance-attributes
class OAuthAuthorizationParams:
"""Parameteres required to authorize an OAuth Client"""
"""Parameters required to authorize an OAuth Client"""
client_id: str
redirect_uri: str
@ -284,7 +284,7 @@ class AuthorizationFlowInitView(PolicyAccessView):
self.params = OAuthAuthorizationParams.from_request(self.request)
except AuthorizeError as error:
LOGGER.warning(error.description, redirect_uri=error.redirect_uri)
raise RequestValidationError(HttpResponseRedirect(error.create_uri()))
raise RequestValidationError(error.get_response(self.request))
except OAuth2Error as error:
LOGGER.warning(error.description)
raise RequestValidationError(
@ -301,7 +301,7 @@ class AuthorizationFlowInitView(PolicyAccessView):
self.params.state,
)
error.to_event(redirect_uri=error.redirect_uri).from_http(self.request)
raise RequestValidationError(HttpResponseRedirect(error.create_uri()))
raise RequestValidationError(error.get_response(self.request))
def resolve_provider_application(self):
client_id = self.request.GET.get("client_id")
@ -463,7 +463,7 @@ class OAuthFulfillmentStage(StageView):
except AuthorizeError as error:
error.to_event(application=self.application).from_http(request)
self.executor.stage_invalid()
return self.redirect(error.create_uri())
return error.get_response(self.request)
def create_response_uri(self) -> str:
"""Create a final Response URI the user is redirected to."""

View File

@ -1,12 +1,18 @@
"""authentik pretend GitHub Views"""
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils.decorators import method_decorator
from django.utils.text import slugify
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from authentik.providers.oauth2.constants import SCOPE_GITHUB_ORG_READ, SCOPE_GITHUB_USER_EMAIL
from authentik.providers.oauth2.models import RefreshToken
from authentik.providers.oauth2.utils import protected_resource_view
@method_decorator(csrf_exempt, name="dispatch")
@method_decorator(protected_resource_view([SCOPE_GITHUB_USER_EMAIL]), name="dispatch")
class GitHubUserView(View):
"""Emulate GitHub's /user API Endpoint"""
@ -62,6 +68,8 @@ class GitHubUserView(View):
)
@method_decorator(csrf_exempt, name="dispatch")
@method_decorator(protected_resource_view([SCOPE_GITHUB_ORG_READ]), name="dispatch")
class GitHubUserTeamsView(View):
"""Emulate GitHub's /user/teams API Endpoint"""

View File

@ -2,7 +2,9 @@
from dataclasses import dataclass, field
from django.http import HttpRequest, HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from structlog.stdlib import get_logger
from authentik.providers.oauth2.errors import TokenIntrospectionError
@ -59,6 +61,7 @@ class TokenIntrospectionParams:
return TokenIntrospectionParams(token=token, provider=provider)
@method_decorator(csrf_exempt, name="dispatch")
class TokenIntrospectionView(View):
"""Token Introspection
https://tools.ietf.org/html/rfc7662"""

View File

@ -1,12 +1,14 @@
"""authentik OAuth2 JWKS Views"""
from base64 import urlsafe_b64encode
from base64 import b64encode, urlsafe_b64encode
from typing import Optional
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.ec import (
EllipticCurvePrivateKey,
EllipticCurvePublicKey,
)
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
from cryptography.hazmat.primitives.serialization import Encoding
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from django.views import View
@ -30,12 +32,13 @@ class JWKSView(View):
def get_jwk_for_key(self, key: CertificateKeyPair) -> Optional[dict]:
"""Convert a certificate-key pair into JWK"""
private_key = key.private_key
key_data = None
if not private_key:
return None
return key_data
if isinstance(private_key, RSAPrivateKey):
public_key: RSAPublicKey = private_key.public_key()
public_numbers = public_key.public_numbers()
return {
key_data = {
"kty": "RSA",
"alg": JWTAlgorithms.RS256,
"use": "sig",
@ -43,10 +46,10 @@ class JWKSView(View):
"n": b64_enc(public_numbers.n),
"e": b64_enc(public_numbers.e),
}
if isinstance(private_key, EllipticCurvePrivateKey):
elif isinstance(private_key, EllipticCurvePrivateKey):
public_key: EllipticCurvePublicKey = private_key.public_key()
public_numbers = public_key.public_numbers()
return {
key_data = {
"kty": "EC",
"alg": JWTAlgorithms.ES256,
"use": "sig",
@ -54,7 +57,20 @@ class JWKSView(View):
"n": b64_enc(public_numbers.n),
"e": b64_enc(public_numbers.e),
}
return None
else:
return key_data
key_data["x5c"] = [b64encode(key.certificate.public_bytes(Encoding.DER)).decode("utf-8")]
key_data["x5t"] = (
urlsafe_b64encode(key.certificate.fingerprint(hashes.SHA1())) # nosec
.decode("utf-8")
.rstrip("=")
)
key_data["x5t#S256"] = (
urlsafe_b64encode(key.certificate.fingerprint(hashes.SHA256()))
.decode("utf-8")
.rstrip("=")
)
return key_data
def get(self, request: HttpRequest, application_slug: str) -> HttpResponse:
"""Show JWK Key data for Provider"""

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