Compare commits

...

263 Commits

Author SHA1 Message Date
632e3cf7dc Merge branch 'master' into version-2021.12 2021-12-01 15:27:48 +01:00
e7144649d5 ci: dont use matrix for multiplatform build
This reverts commit 9092d1189b.

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

# Conflicts:
#	.github/workflows/ci-main.yml
#	.github/workflows/ci-outpost.yml
#	.github/workflows/release-publish.yml
2021-12-01 15:27:37 +01:00
dd8909c9b2 website/docs: add v2021.12 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-01 13:23:55 +01:00
e6818c1f6a release: 2021.12.1-rc1 2021-12-01 13:08:13 +01:00
10c4e3c717 ci: use buildx
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-01 13:08:06 +01:00
b8425867c8 build(deps): bump chart.js from 3.6.0 to 3.6.1 in /web (#1864) 2021-12-01 08:54:36 +01:00
a05da8cdbf build(deps): bump rollup from 2.60.1 to 2.60.2 in /web (#1865) 2021-12-01 08:54:08 +01:00
c3aeefa653 website/docs: fix wrong placeholder
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-30 11:21:14 +01:00
62c840df21 website/docs: fix missing placeholder
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-30 11:20:05 +01:00
45d1db8880 website/docs: add proxy custom header docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-30 11:12:34 +01:00
b34f30f1dd build(deps): bump @typescript-eslint/eslint-plugin in /web (#1860) 2021-11-30 08:54:12 +01:00
7a54e84eb4 build(deps): bump @typescript-eslint/parser from 5.4.0 to 5.5.0 in /web (#1861) 2021-11-30 08:52:44 +01:00
917eef96fb lib: add improved log to sentry events being sent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 21:37:29 +01:00
9a393848b2 outpost: configure error reporting based off of main instance config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 14:42:19 +01:00
a6abeb50c6 build(deps): bump goauthentik.io/api from 0.2021104.5 to 0.2021104.6 (#1858)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.2021104.5 to 0.2021104.6.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.2021104.5...v0.2021104.6)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-29 14:39:17 +01:00
39acb044fb lifecycle: allow custom worker count in k8s
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 14:27:55 +01:00
7d2f622f4b web: Update Web API Client version (#1857)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 14:17:12 +01:00
a2b38caf64 web: update for new config api
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 13:58:00 +01:00
1193b9fd22 root: revert to upstream api generator
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 13:56:18 +01:00
e3a5ef1907 root: make sentry sample rate configurable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-29 13:52:34 +01:00
e597bb4542 policies/expression: fix ak_user_has_authenticator evaluation when not specifying optional device_type (#1849)
* Fix ak_user_has_authenticator evaluation when not specifying optional device_type

* Simpler patch
2021-11-29 10:35:17 +01:00
c31df2b3f9 build(deps): bump @lingui/cli from 3.12.1 to 3.13.0 in /web (#1854) 2021-11-29 09:00:35 +01:00
3f2637cffa build(deps): bump @lingui/detect-locale from 3.12.1 to 3.13.0 in /web (#1852) 2021-11-29 09:00:25 +01:00
3b6d9bec0a build(deps): bump @lingui/macro from 3.12.1 to 3.13.0 in /web (#1853) 2021-11-29 08:49:01 +01:00
b184210610 build(deps): bump postcss from 8.4.1 to 8.4.4 in /website (#1851) 2021-11-29 08:48:29 +01:00
d2010808ee build(deps): bump @lingui/core from 3.12.1 to 3.13.0 in /web (#1855) 2021-11-29 08:48:03 +01:00
f5b185dd06 build(deps): bump goauthentik.io/api from 0.2021104.3 to 0.2021104.5 (#1856) 2021-11-29 08:47:21 +01:00
ae161c1ba9 web/admin: fix actions column on ip reputation page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 21:14:52 +01:00
109283b189 web: use ak-label for boolean values
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 21:12:53 +01:00
235d283def web/elements: use <slot> in ak-label instead of attribute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 21:05:07 +01:00
96a86b3298 web: include @lit in prettier sort
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 15:45:00 +01:00
db9ea8603c web: cleanup sentry error catching
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 15:17:27 +01:00
8b7f698c7b outposts/proxy: continue compiling additional regexes even when one fails
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-28 15:06:26 +01:00
813c13ce45 web/admin: fix display of banners on provider pages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 20:48:06 +01:00
629a0e1a4d web/admin: make object view pages more consistent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 19:40:40 +01:00
d1e2c018a3 root: fix dockerfile paths
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 14:36:44 +01:00
1e86844823 root: copy website into web builder for docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 14:19:57 +01:00
b58875d4c7 web: add rollup config for proxy outpost
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 14:18:51 +01:00
03e0eecb1d web/admin: redesign provider pages to provide more info
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 14:08:45 +01:00
7aa61d86e4 web: allow markdown import
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 13:52:11 +01:00
0e6a799e6d web/elements: allow multiple tabs with different state
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 13:30:02 +01:00
bc6afdf94f website/docs: use common placeholders for forward_auth
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 13:29:38 +01:00
80364b04a9 web/elements: allow app.model names for ak-object-changelog
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 13:03:13 +01:00
0948e0ee1c web: Update Web API Client version (#1848)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-26 10:35:03 +01:00
5c54de66fc *: add meta_model_name field to all models with inheritance
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 10:32:39 +01:00
937edc73bc web: Update Web API Client version (#1847)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-26 10:30:18 +01:00
2c0d8d8943 core: add meta_model_name to MetaNameSerializer to easily show relevant events
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-26 10:27:08 +01:00
059ccdd592 build(deps-dev): bump prettier from 2.4.1 to 2.5.0 in /website (#1845) 2021-11-26 08:49:08 +01:00
0ec0d3f1aa build(deps): bump prettier from 2.4.1 to 2.5.0 in /web (#1846) 2021-11-26 08:48:26 +01:00
0a0eee138a stages/authenticator_validate: catch error when attempting to configure user without flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-25 23:44:48 +01:00
3ed4c38101 web: re-fix router height
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-25 23:31:35 +01:00
de8cf65503 stages/email: prevent error with duplicate token
closes #1827

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-25 23:17:37 +01:00
121b36f35f lib: log error for file:// in config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-25 19:21:40 +01:00
363aed2a47 root: url quote redis passwords for connection string
closes https://github.com/goauthentik/helm/issues/39

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-25 18:05:36 +01:00
ef994e0084 lifecycle: improve redis connection debug py printing full URL 2021-11-25 13:44:42 +01:00
e1ef196283 core: remove dump_config, handle directly in config loader without booting django, don't check database 2021-11-25 13:38:31 +01:00
f81ffd54f3 website/docs: fix invalid markdown 2021-11-25 13:37:57 +01:00
f9bfae9190 Merge branch 'master' into next 2021-11-25 13:07:55 +01:00
0d686465a4 ci: bump cache revision 2021-11-25 11:54:30 +00:00
e13b4a561f web/user: fix filtering for applications based on launchURL 2021-11-25 11:32:24 +01:00
f6417f95e5 build(deps): bump postcss from 8.3.11 to 8.4.1 in /website (#1841) 2021-11-25 08:17:28 +01:00
9c6bf5f4ae build(deps): bump goauthentik.io/api from 0.2021104.2 to 0.2021104.3 (#1842) 2021-11-25 08:16:39 +01:00
d2d7acb50e website/integrations: update minio callback URL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 22:54:45 +01:00
c7681dde32 outposts: reload on signal USR1, fix display of reload offset
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 22:45:27 +01:00
8cf9661e08 root: fix translation, run translation compile on PR
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 18:16:02 +01:00
2dbd76cf90 tests/e2e: use StaticLiveServerTestCase
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 11:32:45 +01:00
28d39f4d80 website: add netlify badge
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 11:30:02 +01:00
760428aa18 website/docs: add outpost integrations docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 10:58:23 +01:00
49bbac7441 web: Update Web API Client version (#1840)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-24 10:04:38 +01:00
0b8cfd437b *: fix typo'd signing pair name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 09:55:10 +01:00
b69aaf9417 tests/e2e: fix header name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 09:18:22 +01:00
758d1bdfd4 tests/e2e: fix typo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-24 08:50:13 +01:00
ab501ca971 build(deps): bump actions/cache from 2.1.6 to 2.1.7 (#1838) 2021-11-24 07:38:33 +01:00
9657741a3d build(deps): bump github.com/go-openapi/strfmt from 0.21.0 to 0.21.1 (#1839) 2021-11-24 07:38:02 +01:00
29b7368f42 tests/e2e: fix static user checks
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:56:23 +01:00
75724b6f8d root: make testing output more consistent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:46:27 +01:00
7c9f821bfd web: attempt to drop fetch errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:29:25 +01:00
5b9e6bed6c lib: fix custom URL schemes being overwritten
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:23:09 +01:00
6113d7d768 website/docs: add application docs
closes #1837

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:15:30 +01:00
0e3602d7eb lib: improve probability of symbols in generated key
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 23:01:30 +01:00
2b94e9a687 tests/e2e: bump retries
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:54:08 +01:00
6ed7d842e4 *: allow URLs without domain and custom schemas
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:51:04 +01:00
8794c840cf web: only show applications with http link
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:40:31 +01:00
9c9c00755a core: fix test user not having password set properly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:30:09 +01:00
6703c0a5d1 tests/e2e: don't load core migration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 22:05:19 +01:00
060f19ce06 tests/e2e: ensure akadmin is not used
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 21:34:53 +01:00
b2d2e7cbc8 tests/e2e: remove logger
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 21:19:33 +01:00
91fd792f88 tests/e2e: use generated uid
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 19:19:13 +01:00
2d9cd28221 tests/e2e: bump retries
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 19:15:37 +01:00
aa64cf898f ci: enable automerge for generated PRs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 19:15:31 +01:00
27d109c1fe core: compile backend translations (#1836)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-23 19:12:48 +01:00
1b4a14f3ee root: allow .mo files for backend
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 19:09:29 +01:00
9835785864 core: make test user's password optional
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 19:06:44 +01:00
d785998c5a Revert "root: disable random tests for now"
This reverts commit 8ba9553220.
2021-11-23 18:46:51 +01:00
8ba9553220 root: disable random tests for now
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 17:57:56 +01:00
6eb132c48b tests/e2e: fix ldap provider tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 17:28:35 +01:00
b523cd064b Translate /locale/en/LC_MESSAGES/django.po in de (#1834) 2021-11-23 15:17:57 +01:00
355b832cc3 tests/e2e: fix email backend
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 13:22:28 +01:00
8f5af464a2 web/admin: fix Forms with file uploads not handling errors correctly
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 12:18:39 +01:00
fb70769358 root: add missing importlib-metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 10:11:47 +01:00
ad06778c34 ci: remove v2 suffix in cache
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 10:00:23 +01:00
bcb4451fb7 build(deps): bump rollup from 2.60.0 to 2.60.1 in /web (#1832) 2021-11-23 08:52:34 +01:00
110d558572 build(deps): bump boto3 from 1.20.10 to 1.20.11 (#1833) 2021-11-23 08:47:20 +01:00
e32d4f0095 tests/e2e: don't run e2e tests randomly for now
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 00:32:24 +01:00
0e413acd61 ci: only try once for now
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 00:30:10 +01:00
d3397c349f stages/email: minify email css template
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-23 00:10:43 +01:00
fb18a10e61 website/integrations: Add Provider/Uptime Kuma (#1831)
* docs: add integration docs for uptime-kuma

* docs: add integration docs for uptime-kuma
2021-11-23 00:10:31 +01:00
9bb0d04aeb root: Random tests (#1825)
* root: add pytest-randomly to randomise tests

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

* *: generate flows for testing instead of relying on existing ones

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

* *: generate users for testing instead of relying on existing ones

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

* *: use generated certificate

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

* tests/e2e: keep containers

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

* tests/e2e: use websockets test case

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 22:56:02 +01:00
666cf77b04 website/integrations: add integration docs for budibase (#1830) 2021-11-22 22:55:49 +01:00
90ca1b8e5a website/integrations: Add Provider/HedgeDoc (#1829)
* docs: add integration docs for hedgedoc

* docs: add integration docs for hedgedoc
2021-11-22 22:55:14 +01:00
f1e95b8816 website/integrations: Add Provider/PowerDNS-Admin (#1826)
* docs: add integration docs for powerdns-admin

* docs: add integration docs for powerdns-admin
2021-11-22 21:02:30 +01:00
dad8547212 root: remove arm/v7
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 14:29:21 +01:00
a957e1fc45 root: install cargo for cryptography build
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 12:22:19 +01:00
39e3f02503 website: fix build for docs-only target
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 12:11:24 +01:00
2b999e922c ci: disable arm for ci due to duration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 11:48:41 +01:00
4224134a19 tests/e2e: remove deprecated desired_capabilities
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 11:28:26 +01:00
eda260dddd website: fix redirect
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 11:13:31 +01:00
8a1dd521e1 website: move integrations to separate folder, separate sidebar and new URL, add URL redirect
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 11:10:26 +01:00
1c5e91de1d website: fix selection in navbar not being visible
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 10:55:33 +01:00
4b1744fad0 website/docs: add onlyoffice docs
closes #1820

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 10:28:21 +01:00
f17b83010d root: remove separate postgresql repo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-22 10:07:26 +01:00
12ddf9e73c build(deps): bump boto3 from 1.20.8 to 1.20.10 (#1823) 2021-11-22 09:00:27 +01:00
0b3b300333 build(deps): bump eslint from 8.2.0 to 8.3.0 in /web (#1821) 2021-11-22 08:59:32 +01:00
23f1a19765 build(deps): bump codemirror from 5.63.3 to 5.64.0 in /web (#1822) 2021-11-22 08:58:54 +01:00
b27e998615 build(deps): bump structlog from 21.2.0 to 21.3.0 (#1824) 2021-11-22 08:58:04 +01:00
2b928146a8 root: use amd64 for multistage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 23:59:05 +01:00
a94b0504b7 ci: always disable fail-fast
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 23:44:09 +01:00
4fcbfa7709 ci: add missing qemu action
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 23:35:23 +01:00
986e01db20 root: add missing libraries to compile cryptography for armv7
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 23:34:01 +01:00
9092d1189b ci: disable arm/v7 for now, use matrix for release
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 23:08:55 +01:00
605ed94ba2 ci: use matrix for tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 22:51:07 +01:00
4cbeeb9a0c ci: add cross platform build for dev main image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#1819
2021-11-21 22:44:49 +01:00
993dee6aad ci: build outpost for multi arch in matrix
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 22:33:43 +01:00
c663deb659 website/docs: note to not use quotation marks for email
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-21 21:52:29 +01:00
61621e7d60 lifecycle: improve backup restore by dropping database before
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-20 00:32:24 +01:00
0ee9b07172 web/admin: show changelog on user info page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:59:04 +01:00
431ba6b4ef lib: add cli option for lib.config
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:52:10 +01:00
146818793e website/docs: fix kubectl restart command
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:45:03 +01:00
0ce663bce4 web/user: fix height issues on user interface
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:32:25 +01:00
923ba4fb42 web: improve dark theme for vertical tabs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:29:47 +01:00
bb6eed0db1 root: properly catch 404 errors for websocket connections
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 23:19:07 +01:00
d1bd8f333b outposts/proxy: use disableIndex for static files
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 10:50:56 +01:00
2ac9f5426d outposts: don't panic when listening for metrics fails
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 10:37:13 +01:00
8d1fd48003 web/admin: allow flow edit on flow view page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 10:20:31 +01:00
241cb01ec6 web/flows: fix spinner during webauthn not centred
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-19 09:51:52 +01:00
65b4139997 build(deps): bump @patternfly/patternfly from 4.151.4 to 4.159.1 in /web (#1816) 2021-11-19 08:46:35 +01:00
1431be8c44 build(deps): bump geoip2 from 4.4.0 to 4.5.0 (#1817) 2021-11-19 08:45:20 +01:00
049fceeeee web: add more state
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 21:40:34 +01:00
e6638afa3c web: remove manually URL encoded paths
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 21:33:49 +01:00
465898c7d0 web/elements: add new API to store attributes in URL, use for table and tabs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 21:16:00 +01:00
c363b1cfde web/admin: unify rendering and sorting of user lists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 20:44:15 +01:00
b30ffd1318 web/admin: make user clickable for bound policies list
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 20:43:45 +01:00
fe0d3a64c8 web/admin: fix typo in events action
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 20:32:59 +01:00
ae9f1c1063 outpost/ldap: fix panic when attempting to update without locked users mutex
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 19:36:27 +01:00
ea63d384fd web/flows: fix lint errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-18 09:36:42 +01:00
c28d75754d build(deps): bump boto3 from 1.20.7 to 1.20.8 (#1813) 2021-11-18 08:50:55 +01:00
518b691e00 build(deps): bump packaging from 21.2 to 21.3 (#1812) 2021-11-18 08:50:15 +01:00
cd845be45d build(deps): bump typescript from 4.4.4 to 4.5.2 in /web (#1811) 2021-11-18 08:49:45 +01:00
a813d8e05e build(deps): bump sentry-sdk from 1.4.3 to 1.5.0 (#1814) 2021-11-18 08:49:18 +01:00
75f850f4d2 build(deps): bump @babel/plugin-transform-runtime in /web (#1804)
Bumps [@babel/plugin-transform-runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-runtime) from 7.16.0 to 7.16.4.
- [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.16.4/packages/babel-plugin-transform-runtime)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:40:32 +01:00
c84265c6f0 build(deps): bump @sentry/browser from 6.14.3 to 6.15.0 in /web (#1805)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.14.3 to 6.15.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/6.14.3...6.15.0)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:39:21 +01:00
a477ea29cd build(deps): bump @babel/preset-env from 7.16.0 to 7.16.4 in /web (#1803)
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.16.0 to 7.16.4.
- [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.16.4/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>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:39:06 +01:00
f6aa85e340 build(deps): bump @sentry/tracing from 6.14.3 to 6.15.0 in /web (#1806)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.14.3 to 6.15.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/6.14.3...6.15.0)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:38:02 +01:00
0aeedb3ad8 build(deps): bump @babel/plugin-proposal-decorators in /web (#1807)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.16.0 to 7.16.4.
- [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.16.4/packages/babel-plugin-proposal-decorators)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:37:53 +01:00
4b29f238b5 build(deps): bump boto3 from 1.20.6 to 1.20.7 (#1808)
Bumps [boto3](https://github.com/boto/boto3) from 1.20.6 to 1.20.7.
- [Release notes](https://github.com/boto/boto3/releases)
- [Changelog](https://github.com/boto/boto3/blob/develop/CHANGELOG.rst)
- [Commits](https://github.com/boto/boto3/compare/1.20.6...1.20.7)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:37:16 +01:00
34157db06a build(deps): bump celery from 5.2.0 to 5.2.1 (#1809)
Bumps [celery](https://github.com/celery/celery) from 5.2.0 to 5.2.1.
- [Release notes](https://github.com/celery/celery/releases)
- [Changelog](https://github.com/celery/celery/blob/master/Changelog.rst)
- [Commits](https://github.com/celery/celery/compare/v5.2.0...v5.2.1)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:37:03 +01:00
84b9e66a97 build(deps): bump goauthentik.io/api from 0.2021104.1 to 0.2021104.2 (#1810)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.2021104.1 to 0.2021104.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.2021104.1...v0.2021104.2)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-17 09:36:47 +01:00
e831e4fb94 root: add lifespan shim to prevent errors
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-16 13:25:03 +01:00
956922820b web: Update Web API Client version (#1802)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-16 12:39:37 +01:00
b0fac9c9f1 providers/saml: fix SessionNotOnOrAfter not being included
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-16 12:36:40 +01:00
f4db09cd59 events: add gdpr_compliance option
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#1551
2021-11-16 11:29:13 +01:00
047030f901 web: optionally allow unique messages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-16 11:21:30 +01:00
638e8d741f *: fix multiple tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-16 10:38:21 +01:00
425b87a6d0 outposts: add ack and disconnect tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-16 09:34:37 +01:00
e7dc763612 build(deps): bump @typescript-eslint/eslint-plugin in /web (#1797) 2021-11-16 08:22:27 +01:00
a80cc94da9 build(deps): bump @typescript-eslint/parser from 5.3.1 to 5.4.0 in /web (#1798) 2021-11-16 08:21:19 +01:00
547dd3cb7a build(deps): bump goauthentik.io/api from 0.2021103.2 to 0.2021104.1 (#1799) 2021-11-16 08:20:30 +01:00
95739a934c build(deps): bump boto3 from 1.20.5 to 1.20.6 (#1800) 2021-11-16 08:20:07 +01:00
d12e24017e outposts: add websocket tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 23:58:19 +01:00
e4a0345231 tests/integration: use channels test server
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 22:37:36 +01:00
078633c2af lib: drop all sentry exceptions when debug enabled
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 22:18:56 +01:00
4b8b800648 stages/*: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 20:58:34 +01:00
6f9ed001a1 crypto: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 20:38:03 +01:00
e4095dfffe admin: add more tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 20:10:09 +01:00
d5341c2284 managed: add tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 19:53:08 +01:00
357bd65028 web/flows: fix typo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 19:52:17 +01:00
867fb0dac0 root: fix settings for managed not loaded
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 19:49:03 +01:00
2666aa2c73 root: add errorhandling in log middleware
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 17:11:44 +01:00
f0e9bafa35 outposts: add tests for management commands
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 16:44:42 +01:00
0d739f5c1a recovery: add additional tests for commands
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 16:41:37 +01:00
e08077c73a root: replace asgi-based logger with middleware
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 16:32:56 +01:00
7cf8a31057 internal: fix integrated docs not working
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 16:13:02 +01:00
c43049a981 website/docs: remove deprecated docker_image_base
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 15:58:17 +01:00
1a9ace6f9d internal: use runserver when debug for code reload
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 14:04:10 +01:00
b8d86bc482 web/flows: update default background
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 13:07:00 +01:00
f7044e41c6 build(deps-dev): bump bandit from 1.7.0 to 1.7.1 (#1793)
* build(deps-dev): bump bandit from 1.7.0 to 1.7.1

Bumps [bandit](https://github.com/PyCQA/bandit) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/PyCQA/bandit/releases)
- [Commits](https://github.com/PyCQA/bandit/compare/1.7.0...1.7.1)

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

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

* *: fix bandit false positives

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-15 09:16:16 +01:00
fa59fec17a build(deps): bump rollup from 2.59.0 to 2.60.0 in /web (#1792) 2021-11-15 08:39:31 +01:00
e29afa289e build(deps): bump boto3 from 1.20.4 to 1.20.5 (#1794) 2021-11-15 08:39:14 +01:00
4d4193a586 ci: re-add cache
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-14 19:14:18 +01:00
59343ff441 stages/email: fix missing component in response when retrying email send
closes #1791

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-14 17:52:31 +01:00
cab564152d lib: load json strings in config env variables
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-14 12:44:22 +01:00
97b814ab33 outpost/proxy: show better error when hostname isn't configured
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-13 22:45:37 +01:00
88516ba2ca core: make defaults for _change_email and _change_username configurable
closes #1789

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-13 22:33:03 +01:00
f069cfb643 outposts/ldap: copy boundUsers map when running refresh instead of using blank map
closes #1651

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-13 00:26:01 +01:00
4ce3c2341c website/docs: add nginx-proxy-manager
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-12 23:00:10 +01:00
77e42d60cb website/docs: use new headers in docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-12 22:48:01 +01:00
cacb919c6f web: Update Web API Client version (#1787)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-12 14:15:26 +01:00
2a3b049b01 release: 2021.10.4 2021-11-12 12:31:24 +01:00
e4a5e86c93 sources/oauth: Fixed the incorrect padding issue in apple.py (#1773)
* Fixed the incorrect padding issue in apple.py

Fixed the incorrect padding issue in apple.py by adding proper padding to the raw_payload.

* Fixed the incorrect encoding of client_secret in apple.py

In the get_client_secret() method, the "sub" in the payload must be only the client ID. So I have changed self.source.consumer_key to parts[0]

* Added the decode method for the id_token

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-12 12:10:29 +01:00
3a51bcd890 tests/e2e: add retry for webdriver init
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-12 09:37:05 +01:00
c28f68400d build(deps): bump @sentry/tracing from 6.14.1 to 6.14.3 in /web (#1783) 2021-11-12 08:30:16 +01:00
5d50fc281a build(deps): bump boto3 from 1.20.3 to 1.20.4 (#1785) 2021-11-12 08:30:02 +01:00
9f7d1466e9 build(deps): bump @sentry/browser from 6.14.1 to 6.14.3 in /web (#1784) 2021-11-12 08:29:22 +01:00
c815d24806 build(deps): bump psycopg2-binary from 2.9.1 to 2.9.2 (#1786) 2021-11-12 08:29:05 +01:00
d1200a7e40 website/docs: Mention correct logo in Gitea docs (#1782) 2021-11-12 01:02:17 +01:00
edd4f9ceae root: update security
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 23:50:46 +01:00
1cfe81887b stages/authenticator_validate: improve logging
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 23:33:41 +01:00
bb5e0ebab1 website/docs: Add Integrations/Provider/Gitea (#1781) 2021-11-11 23:23:32 +01:00
dfda76d896 tests/e2e: use cached LDAP lookup for tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 23:20:32 +01:00
8fc5114ce4 website/docs: prepare 2021.10.4 docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 23:20:17 +01:00
e7b4363d21 outposts/ldap: fix logic error in cached ldap searcher
closes #1779

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 23:18:32 +01:00
53905d1a89 stages/authenticator_validate: enable all device classes by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 22:49:30 +01:00
0ad1392632 web/admin: use more natural default ordering for objects
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 22:47:10 +01:00
6db1c914ee stages/authenticator_duo: fix devices created with name
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 22:16:47 +01:00
00324f922d outposts: send SelectedChallenge when using MFA with Go FlowExecutor
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 21:27:06 +01:00
8a24ddad28 website/docs: Added missing SSO server URL field for Zabbix (#1780) 2021-11-11 21:06:33 +01:00
0f85fe3c29 website/docs: authentik starts lowercase (#1778)
* website/docs: Add Integrations/Provider/OPNsense

* website/docs: Add missing steps + fix recs

* website/docs: authentik starts lowercase

* website/docs: authentik starts lowercase
2021-11-11 16:53:46 +01:00
1f05eaa420 website/docs: Add Integrations/Provider/OPNsense (#1777)
* website/docs: Add Integrations/Provider/OPNsense

* website/docs: Add missing steps + fix recs

* website/docs: authentik starts lowercase
2021-11-11 16:44:02 +01:00
84e126a32c website/docs: add group hierarchy docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 16:15:40 +01:00
9ae69866bd web/admin: fix display issues with flow execute buttons
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-11 13:51:33 +01:00
56576a7f44 build(deps): bump boto3 from 1.20.2 to 1.20.3 (#1769) 2021-11-11 08:43:22 +01:00
7f0295ba53 build(deps): bump @trivago/prettier-plugin-sort-imports in /web (#1768) 2021-11-11 08:43:13 +01:00
5553b3ff36 build(deps): bump drf-spectacular from 0.20.2 to 0.21.0 (#1771) 2021-11-11 08:43:03 +01:00
6f969525fe build(deps): bump webauthn from 1.0.1 to 1.1.0 (#1770) 2021-11-11 08:42:33 +01:00
bac12246fb build(deps-dev): bump coverage from 6.1.1 to 6.1.2 (#1772) 2021-11-11 08:42:19 +01:00
b53ef6e529 build(deps): bump goauthentik.io/api from 0.2021103.1 to 0.2021103.2 (#1767)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.2021103.1 to 0.2021103.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.2021103.1...v0.2021103.2)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-10 23:26:13 +01:00
39c62afb93 web: Update Web API Client version (#1766)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-10 23:11:26 +01:00
c98bdbacc5 providers/proxy: return list of configured scope names so outpost requests custom scopes
closes #1762

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 23:06:21 +01:00
1e8d45dc15 web: write interfaces to different folders and remove custom chunk names
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 17:54:50 +01:00
202b057ce9 outposts/proxy: fix static files not being served in proxy mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 17:16:13 +01:00
d5d8641b37 stages/*: disable trim_whitespace on important fields
closes #1765

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 16:48:19 +01:00
9dd37689e3 ci: remove cache from translation compile
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 13:19:57 +01:00
cc0832f487 core: force lowercase emails for gravatar usage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 12:40:20 +01:00
b515bf7d2e ci: disable cache again...
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 11:57:22 +01:00
34fbf3941b website/docs: add air-gapped docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 11:28:59 +01:00
e73606b54d root: catch error in analytics on startup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 11:28:08 +01:00
0a413fe21a web/admin: show warnings above tab bar
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 10:05:35 +01:00
d1b9f1e6b8 ci: limit pipeline to 2 hours
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-10 09:31:59 +01:00
e5a6e128e4 build(deps): bump boto3 from 1.20.0 to 1.20.2 (#1763) 2021-11-10 08:16:19 +01:00
9295d1ed0b website/docs: fix missing SAML cert in sentry docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 23:42:16 +01:00
5d479a6c8f root: set utm_source
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 23:23:47 +01:00
4a773b2b4f sources/ldap: set connect/receive timeout (default to 15s)
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 22:27:58 +01:00
8003d67844 sources/ldap: fix typo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 22:24:33 +01:00
58baf97e2d website/docs: Add additionalHeaders to attributes list (#1754)
* Add additionalHeaders to attributes list

Added additional headers with example of usage

* Update user.md
2021-11-09 21:17:36 +01:00
51783c1cbb sorces/ldap: fix user/group sync overwriting attributes instead of merging them
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 21:16:59 +01:00
94290c7e36 root: remove pipenv constraint
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 14:19:33 +01:00
123ff7ad1f website/docs: Fix typo (#1761) 2021-11-09 13:27:52 +01:00
8f3e863cce root: use python slim-bullseye as base
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-09 11:34:42 +01:00
3d6c459349 build(deps): bump @typescript-eslint/parser from 5.3.0 to 5.3.1 in /web (#1756) 2021-11-09 08:24:51 +01:00
6a583bae49 build(deps): bump goauthentik.io/api from 0.2021102.6 to 0.2021103.1 (#1758) 2021-11-09 08:24:18 +01:00
78e5879d9a build(deps): bump boto3 from 1.19.12 to 1.20.0 (#1757) 2021-11-09 08:24:09 +01:00
fdcac2a9ed build(deps): bump @typescript-eslint/eslint-plugin in /web (#1755) 2021-11-09 08:23:47 +01:00
e81715caef web: Update Web API Client version (#1753)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-11-08 21:19:00 +01:00
414 changed files with 8865 additions and 4197 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2021.10.3
current_version = 2021.12.1-rc1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)

View File

@ -18,79 +18,16 @@ env:
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
jobs:
lint-pylint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: run pylint
run: pipenv run pylint authentik tests lifecycle
lint-black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: run black
run: pipenv run black --check authentik tests lifecycle
lint-isort:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: run isort
run: pipenv run isort --check authentik tests lifecycle
lint-bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: run bandit
run: pipenv run bandit -r authentik tests lifecycle
lint-pyright:
lint:
strategy:
fail-fast: false
matrix:
job:
- pylint
- black
- isort
- bandit
- pyright
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@ -100,12 +37,17 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: '16'
- id: cache-pipenv
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
- name: prepare
run: |
scripts/ci_prepare.sh
npm install -g pyright@1.1.136
- name: run bandit
run: pipenv run pyright e2e lifecycle
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: run pylint
run: pipenv run make ci-${{ matrix.job }}
test-migrations:
runs-on: ubuntu-latest
steps:
@ -114,7 +56,7 @@ jobs:
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -138,7 +80,7 @@ jobs:
run: |
python ./scripts/gh_env.py
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -147,8 +89,8 @@ jobs:
# Copy current, latest config to local
cp authentik/lib/default.yml local.env.yml
git checkout $(git describe --abbrev=0 --match 'version/*')
git checkout ${{ steps.ev.outputs.branchName }} -- .github
git checkout ${{ steps.ev.outputs.branchName }} -- scripts
git checkout $GITHUB_HEAD_REF -- .github
git checkout $GITHUB_HEAD_REF -- scripts
- name: prepare
env:
INSTALL: ${{ steps.cache-pipenv.outputs.cache-hit }}
@ -162,7 +104,7 @@ jobs:
run: |
set -x
git fetch
git checkout ${{ steps.ev.outputs.branchName }}
git checkout $GITHUB_HEAD_REF
pipenv sync --dev
- name: prepare
env:
@ -178,7 +120,7 @@ jobs:
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -207,7 +149,7 @@ jobs:
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -246,7 +188,7 @@ jobs:
with:
domain: ${{github.repository_owner}}
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -257,7 +199,7 @@ jobs:
scripts/ci_prepare.sh
docker-compose -f tests/e2e/docker-compose.yml up -d
- id: cache-web
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/**') }}
@ -279,19 +221,23 @@ jobs:
uses: codecov/codecov-action@v2
build:
needs:
- lint-pylint
- lint-black
- lint-isort
- lint-bandit
- lint-pyright
- lint
- test-migrations
- test-migrations-from-stable
- test-unittest
- test-integration
- test-e2e
runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
arch:
- 'linux/amd64'
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: prepare variables
@ -316,3 +262,4 @@ jobs:
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.sha }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
platforms: ${{ matrix.arch }}

View File

@ -31,16 +31,22 @@ jobs:
golangci/golangci-lint:v1.39.0 \
golangci-lint run -v --timeout 200s
build:
timeout-minutes: 120
needs:
- lint-golint
strategy:
fail-fast: false
matrix:
type:
- proxy
- ldap
arch:
- 'linux/amd64'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: prepare variables
@ -67,3 +73,4 @@ jobs:
file: ${{ matrix.type }}.Dockerfile
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
platforms: ${{ matrix.arch }}

View File

@ -30,14 +30,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2021.10.3,
beryju/authentik:2021.12.1-rc1,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.10.3,
ghcr.io/goauthentik/server:2021.12.1-rc1,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.10.3', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2021.12.1-rc1', 'rc') }}
run: |
docker pull beryju/authentik:latest
docker tag beryju/authentik:latest beryju/authentik:stable
@ -45,8 +45,14 @@ jobs:
docker pull ghcr.io/goauthentik/server:latest
docker tag ghcr.io/goauthentik/server:latest ghcr.io/goauthentik/server:stable
docker push ghcr.io/goauthentik/server:stable
build-proxy:
build-outpost:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
type:
- proxy
- ldap
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
@ -72,68 +78,25 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-proxy:2021.10.3,
beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.10.3,
ghcr.io/goauthentik/proxy:latest
file: proxy.Dockerfile
beryju/authentik-${{ matrix.type }}:2021.12.1-rc1,
beryju/authentik-${{ matrix.type }}:latest,
ghcr.io/goauthentik/${{ matrix.type }}:2021.12.1-rc1,
ghcr.io/goauthentik/${{ matrix.type }}:latest
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.10.3', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2021.12.1-rc1', 'rc') }}
run: |
docker pull beryju/authentik-proxy:latest
docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable
docker push beryju/authentik-proxy:stable
docker pull ghcr.io/goauthentik/proxy:latest
docker tag ghcr.io/goauthentik/proxy:latest ghcr.io/goauthentik/proxy:stable
docker push ghcr.io/goauthentik/proxy:stable
build-ldap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: "^1.15"
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker Login Registry
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-ldap:2021.10.3,
beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.10.3,
ghcr.io/goauthentik/ldap:latest
file: ldap.Dockerfile
platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.10.3', 'rc') }}
run: |
docker pull beryju/authentik-ldap:latest
docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable
docker push beryju/authentik-ldap:stable
docker pull ghcr.io/goauthentik/ldap:latest
docker tag ghcr.io/goauthentik/ldap:latest ghcr.io/goauthentik/ldap:stable
docker push ghcr.io/goauthentik/ldap:stable
docker pull beryju/authentik-${{ matrix.type }}:latest
docker tag beryju/authentik-${{ matrix.type }}:latest beryju/authentik-${{ matrix.type }}:stable
docker push beryju/authentik-${{ matrix.type }}:stable
docker pull ghcr.io/goauthentik/${{ matrix.type }}:latest
docker tag ghcr.io/goauthentik/${{ matrix.type }}:latest ghcr.io/goauthentik/${{ matrix.type }}:stable
docker push ghcr.io/goauthentik/${{ matrix.type }}:stable
test-release:
needs:
- build-server
- build-proxy
- build-ldap
- build-outpost
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@ -170,7 +133,7 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2021.10.3
version: authentik@2021.12.1-rc1
environment: beryjuorg-prod
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -15,6 +15,7 @@ jobs:
run: |
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
docker buildx install
docker build \
--no-cache \
-t testing:latest \

View File

@ -4,6 +4,9 @@ on:
branches: [ master ]
paths:
- '/locale/'
pull_request:
paths:
- '/locale/'
schedule:
- cron: "0 */2 * * *"
workflow_dispatch:
@ -22,7 +25,7 @@ jobs:
with:
python-version: '3.9'
- id: cache-pipenv
uses: actions/cache@v2.1.6
uses: actions/cache@v2.1.7
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-v2-${{ hashFiles('**/Pipfile.lock') }}
@ -37,10 +40,19 @@ jobs:
run: pipenv run ./manage.py compilemessages
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
id: cpr
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: compile-backend-translation
commit-message: "core: compile backend translations"
title: "core: compile backend translations"
body: "core: compile backend translations"
delete-branch: true
signoff: true
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

View File

@ -30,10 +30,19 @@ jobs:
npm i @goauthentik/api@$VERSION
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
id: cpr
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update-web-api-client
commit-message: "web: Update Web API Client version"
title: "web: Update Web API Client version"
body: "web: Update Web API Client version"
delete-branch: true
signoff: true
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

4
.gitignore vendored
View File

@ -66,7 +66,9 @@ coverage.xml
unittest.xml
# Translations
*.mo
# Have to include binary mo files as they are annoying to compile at build time
# since a full postgres and redis instance are required
# *.mo
# Django stuff:

View File

@ -10,7 +10,8 @@
"plex",
"saml",
"totp",
"webauthn"
"webauthn",
"traefik"
],
"python.linting.pylintEnabled": true,
"todo-tree.tree.showCountsInTree": true,

View File

@ -1,41 +1,42 @@
# Stage 1: Lock python dependencies
FROM docker.io/python:3.9-bullseye as locker
FROM docker.io/python:3.9-slim-bullseye as locker
COPY ./Pipfile /app/
COPY ./Pipfile.lock /app/
WORKDIR /app/
RUN pip install pipenv==2021.5.29 && \
RUN pip install pipenv && \
pipenv lock -r > requirements.txt && \
pipenv lock -r --dev-only > requirements-dev.txt
# Stage 2: Build website
FROM docker.io/node:16 as website-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:16 as website-builder
COPY ./website /static/
COPY ./website /work/website/
ENV NODE_ENV=production
RUN cd /static && npm i && npm run build-docs-only
RUN cd /work/website && npm i && npm run build-docs-only
# Stage 3: Build webui
FROM docker.io/node:16 as web-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:16 as web-builder
COPY ./web /static/
COPY ./web /work/web/
COPY ./website /work/website/
ENV NODE_ENV=production
RUN cd /static && npm i && npm run build
RUN cd /work/web && npm i && npm run build
# Stage 4: Build go proxy
FROM docker.io/golang:1.17.3-bullseye AS builder
WORKDIR /work
COPY --from=web-builder /static/robots.txt /work/web/robots.txt
COPY --from=web-builder /static/security.txt /work/web/security.txt
COPY --from=web-builder /static/dist/ /work/web/dist/
COPY --from=web-builder /static/authentik/ /work/web/authentik/
COPY --from=website-builder /static/help/ /work/website/help/
COPY --from=web-builder /work/web/robots.txt /work/web/robots.txt
COPY --from=web-builder /work/web/security.txt /work/web/security.txt
COPY --from=web-builder /work/web/dist/ /work/web/dist/
COPY --from=web-builder /work/web/authentik/ /work/web/authentik/
COPY --from=website-builder /work/website/help/ /work/website/help/
COPY ./cmd /work/cmd
COPY ./web/static.go /work/web/static.go
@ -47,7 +48,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.9-bullseye
FROM docker.io/python:3.9-slim-bullseye
WORKDIR /
COPY --from=locker /app/requirements.txt /
@ -57,11 +58,10 @@ ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates gnupg git runit && \
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
echo "deb http://apt.postgresql.org/pub/repos/apt bullseye-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
apt-get update && \
apt-get install -y --no-install-recommends libpq-dev postgresql-client build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
apt-get install -y --no-install-recommends \
curl ca-certificates gnupg git runit libpq-dev \
postgresql-client build-essential libxmlsec1-dev \
pkg-config libmaxminddb0 && \
pip install -r /requirements.txt --no-cache-dir && \
apt-get remove --purge -y build-essential git && \
apt-get autoremove --purge -y && \

View File

@ -7,13 +7,13 @@ NPM_VERSION = $(shell python -m scripts.npm_version)
all: lint-fix lint test gen
test-integration:
coverage run manage.py test -v 3 tests/integration
coverage run manage.py test tests/integration
test-e2e:
coverage run manage.py test --failfast -v 3 tests/e2e
coverage run manage.py test tests/e2e
test:
coverage run manage.py test -v 3 authentik
coverage run manage.py test authentik
coverage html
coverage report
@ -33,9 +33,10 @@ lint:
bandit -r authentik tests lifecycle -x node_modules
pylint authentik tests lifecycle
i18n-extract:
i18n-extract: i18n-extract-core web-extract
i18n-extract-core:
./manage.py makemessages --ignore web --ignore internal --ignore web --ignore web-api --ignore website -l en
cd web && npm run extract
gen-build:
./manage.py spectacular --file schema.yml
@ -48,7 +49,7 @@ gen-web:
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
ghcr.io/beryju/openapi-generator generate \
openapitools/openapi-generator-cli generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/web-api \
@ -73,6 +74,7 @@ gen-outpost:
-o /local/api \
-c /local/config.yaml
go mod edit -replace goauthentik.io/api=./api
rm -rf config.yaml ./templates/
gen: gen-build gen-clean gen-web
@ -81,3 +83,33 @@ migrate:
run:
go run -v cmd/server/main.go
web: web-lint-fix web-lint web-extract
web-lint-fix:
cd web && npm run prettier
web-lint:
cd web && npm run lint
cd web && npm run lit-analyse
web-extract:
cd web && npm run extract
# These targets are use by GitHub actions to allow usage of matrix
# which makes the YAML File a lot smaller
ci-pylint:
pylint authentik tests lifecycle
ci-black:
black --check authentik tests lifecycle
ci-isort:
isort --check authentik tests lifecycle
ci-bandit:
bandit -r authentik tests lifecycle
ci-pyright:
pyright e2e lifecycle

16
Pipfile
View File

@ -8,7 +8,10 @@ boto3 = "*"
celery = "*"
channels = "*"
channels-redis = "*"
codespell = "*"
colorama = "*"
dacite = "*"
deepmerge = "*"
defusedxml = "*"
django = "*"
django-dbbackup = { git = 'https://github.com/django-dbbackup/django-dbbackup.git', ref = '9d1909c30a3271c8c9c8450add30d6e0b996e145' }
@ -23,6 +26,7 @@ djangorestframework = "*"
djangorestframework-guardian = "*"
docker = "*"
drf-spectacular = "*"
duo-client = "*"
facebook-sdk = "*"
geoip2 = "*"
gunicorn = "*"
@ -40,19 +44,15 @@ service_identity = "*"
structlog = "*"
swagger-spec-validator = "*"
twisted = "==21.7.0"
ua-parser = "*"
urllib3 = {extras = ["secure"],version = "*"}
uvicorn = {extras = ["standard"],version = "*"}
webauthn = "*"
xmlsec = "*"
duo-client = "*"
ua-parser = "*"
deepmerge = "*"
colorama = "*"
codespell = "*"
[dev-packages]
bandit = "*"
black = "==21.9b0"
black = "==21.11b1"
bump2version = "*"
colorama = "*"
coverage = {extras = ["toml"],version = "*"}
@ -60,5 +60,7 @@ pylint = "*"
pylint-django = "*"
pytest = "*"
pytest-django = "*"
selenium = "*"
pytest-randomly = "*"
requests-mock = "*"
selenium = "*"
importlib-metadata = "*"

857
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,8 @@
| Version | Supported |
| ---------- | ------------------ |
| 2021.8.x | :white_check_mark: |
| 2021.9.x | :white_check_mark: |
| 2021.10.x | :white_check_mark: |
## Reporting a Vulnerability

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.10.3"
__version__ = "2021.12.1-rc1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -86,7 +86,7 @@ class SystemSerializer(PassiveSerializer):
def get_embedded_outpost_host(self, request: Request) -> str:
"""Get the FQDN configured on the embedded outpost"""
outposts = Outpost.objects.filter(managed=MANAGED_OUTPOST)
if not outposts.exists():
if not outposts.exists(): # pragma: no cover
return ""
return outposts.first().config.authentik_host

View File

@ -36,7 +36,7 @@ class TaskSerializer(PassiveSerializer):
are pickled in cache. In that case, just delete the info"""
try:
return super().to_representation(instance)
except AttributeError:
except AttributeError: # pragma: no cover
if isinstance(self.instance, list):
for inst in self.instance:
inst.delete()

View File

@ -23,6 +23,6 @@ class WorkerView(APIView):
"""Get currently connected worker count."""
count = len(CELERY_APP.control.ping(timeout=0.5))
# In debug we run with `CELERY_TASK_ALWAYS_EAGER`, so tasks are ran on the main process
if settings.DEBUG:
if settings.DEBUG: # pragma: no cover
count += 1
return Response({"count": count})

View File

@ -8,6 +8,7 @@ from authentik import __version__
from authentik.core.models import Group, User
from authentik.core.tasks import clean_expired_models
from authentik.events.monitored_tasks import TaskResultStatus
from authentik.managed.tasks import managed_reconcile
class TestAdminAPI(TestCase):
@ -94,5 +95,7 @@ class TestAdminAPI(TestCase):
def test_system(self):
"""Test system API"""
# pyright: reportGeneralTypeIssues=false
managed_reconcile() # pylint: disable=no-value-for-parameter
response = self.client.get(reverse("authentik_api:admin_system"))
self.assertEqual(response.status_code, 200)

View File

@ -3,8 +3,13 @@ from django.core.cache import cache
from django.test import TestCase
from requests_mock import Mocker
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
from authentik.admin.tasks import (
VERSION_CACHE_KEY,
clear_update_notifications,
update_latest_version,
)
from authentik.events.models import Event, EventAction
from authentik.lib.config import CONFIG
RESPONSE_VALID = {
"$schema": "https://version.goauthentik.io/schema.json",
@ -56,3 +61,23 @@ class TestAdminTasks(TestCase):
action=EventAction.UPDATE_AVAILABLE, context__new_version="0.0.0"
).exists()
)
def test_version_disabled(self):
"""Test Update checker while its disabled"""
with CONFIG.patch("disable_update_check", True):
update_latest_version.delay().get()
self.assertEqual(cache.get(VERSION_CACHE_KEY), "0.0.0")
def test_clear_update_notifications(self):
"""Test clear of previous notification"""
Event.objects.create(
action=EventAction.UPDATE_AVAILABLE, context={"new_version": "99999999.9999999.9999999"}
)
Event.objects.create(action=EventAction.UPDATE_AVAILABLE, context={"new_version": "1.1.1"})
Event.objects.create(action=EventAction.UPDATE_AVAILABLE, context={})
clear_update_notifications()
self.assertFalse(
Event.objects.filter(
action=EventAction.UPDATE_AVAILABLE, context__new_version="1.1"
).exists()
)

View File

@ -1,18 +0,0 @@
"""Throttling classes"""
from typing import Type
from django.views import View
from rest_framework.request import Request
from rest_framework.throttling import ScopedRateThrottle
class SessionThrottle(ScopedRateThrottle):
"""Throttle based on session key"""
def allow_request(self, request: Request, view):
if request._request.user.is_superuser:
return True
return super().allow_request(request, view)
def get_cache_key(self, request: Request, view: Type[View]) -> str:
return f"authentik-throttle-session-{request._request.session.session_key}"

View File

@ -5,7 +5,14 @@ from django.conf import settings
from django.db import models
from drf_spectacular.utils import extend_schema
from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME
from rest_framework.fields import BooleanField, CharField, ChoiceField, IntegerField, ListField
from rest_framework.fields import (
BooleanField,
CharField,
ChoiceField,
FloatField,
IntegerField,
ListField,
)
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.response import Response
@ -24,13 +31,19 @@ class Capabilities(models.TextChoices):
CAN_BACKUP = "can_backup"
class ErrorReportingConfigSerializer(PassiveSerializer):
"""Config for error reporting"""
enabled = BooleanField(read_only=True)
environment = CharField(read_only=True)
send_pii = BooleanField(read_only=True)
traces_sample_rate = FloatField(read_only=True)
class ConfigSerializer(PassiveSerializer):
"""Serialize authentik Config into DRF Object"""
error_reporting_enabled = BooleanField(read_only=True)
error_reporting_environment = CharField(read_only=True)
error_reporting_send_pii = BooleanField(read_only=True)
error_reporting = ErrorReportingConfigSerializer(required=True)
capabilities = ListField(child=ChoiceField(choices=Capabilities.choices))
cache_timeout = IntegerField(required=True)
@ -66,9 +79,12 @@ class ConfigView(APIView):
"""Retrieve public configuration options"""
config = ConfigSerializer(
{
"error_reporting_enabled": CONFIG.y("error_reporting.enabled"),
"error_reporting_environment": CONFIG.y("error_reporting.environment"),
"error_reporting_send_pii": CONFIG.y("error_reporting.send_pii"),
"error_reporting": {
"enabled": CONFIG.y("error_reporting.enabled"),
"environment": CONFIG.y("error_reporting.environment"),
"send_pii": CONFIG.y("error_reporting.send_pii"),
"traces_sample_rate": float(CONFIG.y("error_reporting.sample_rate", 0.4)),
},
"capabilities": self.get_capabilities(),
"cache_timeout": int(CONFIG.y("redis.cache_timeout")),
"cache_timeout_flows": int(CONFIG.y("redis.cache_timeout_flows")),

View File

@ -56,6 +56,7 @@ class PropertyMappingSerializer(ManagedSerializer, ModelSerializer, MetaNameSeri
"component",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
]

View File

@ -43,6 +43,7 @@ class ProviderSerializer(ModelSerializer, MetaNameSerializer):
"assigned_application_name",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
]

View File

@ -48,6 +48,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer):
"component",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
"policy_engine_mode",
"user_matching_mode",
]

View File

@ -55,6 +55,7 @@ from authentik.core.models import (
User,
)
from authentik.events.models import EventAction
from authentik.lib.config import CONFIG
from authentik.stages.email.models import EmailStage
from authentik.stages.email.tasks import send_mails
from authentik.stages.email.utils import TemplateEmailMessage
@ -125,7 +126,9 @@ class UserSelfSerializer(ModelSerializer):
def validate_email(self, email: str):
"""Check if the user is allowed to change their email"""
if self.instance.group_attributes().get(USER_ATTRIBUTE_CHANGE_EMAIL, True):
if self.instance.group_attributes().get(
USER_ATTRIBUTE_CHANGE_EMAIL, CONFIG.y_bool("default_user_change_email", True)
):
return email
if email != self.instance.email:
raise ValidationError("Not allowed to change email.")
@ -133,7 +136,9 @@ class UserSelfSerializer(ModelSerializer):
def validate_username(self, username: str):
"""Check if the user is allowed to change their username"""
if self.instance.group_attributes().get(USER_ATTRIBUTE_CHANGE_USERNAME, True):
if self.instance.group_attributes().get(
USER_ATTRIBUTE_CHANGE_USERNAME, CONFIG.y_bool("default_user_change_username", True)
):
return username
if username != self.instance.username:
raise ValidationError("Not allowed to change username.")

View File

@ -41,6 +41,7 @@ class MetaNameSerializer(PassiveSerializer):
verbose_name = SerializerMethodField()
verbose_name_plural = SerializerMethodField()
meta_model_name = SerializerMethodField()
def get_verbose_name(self, obj: Model) -> str:
"""Return object's verbose_name"""
@ -50,6 +51,10 @@ class MetaNameSerializer(PassiveSerializer):
"""Return object's plural verbose_name"""
return obj._meta.verbose_name_plural
def get_meta_model_name(self, obj: Model) -> str:
"""Return internal model name"""
return f"{obj._meta.app_label}.{obj._meta.model_name}"
class TypeCreateSerializer(PassiveSerializer):
"""Types of an object that can be created"""

View File

@ -1,15 +0,0 @@
"""Output full config"""
from json import dumps
from django.core.management.base import BaseCommand, no_translations
from authentik.lib.config import CONFIG
class Command(BaseCommand): # pragma: no cover
"""Output full config"""
@no_translations
def handle(self, *args, **options):
"""Check permissions for all apps"""
print(dumps(CONFIG.raw, indent=4))

View File

@ -12,7 +12,6 @@ LOCAL = local()
RESPONSE_HEADER_ID = "X-authentik-id"
KEY_AUTH_VIA = "auth_via"
KEY_USER = "user"
INTERNAL_HEADER_PREFIX = "X-authentik-internal-"
class ImpersonateMiddleware:
@ -53,9 +52,9 @@ class RequestIDMiddleware:
}
response = self.get_response(request)
response[RESPONSE_HEADER_ID] = request.request_id
if auth_via := LOCAL.authentik.get(KEY_AUTH_VIA, None):
response[INTERNAL_HEADER_PREFIX + KEY_AUTH_VIA] = auth_via
response[INTERNAL_HEADER_PREFIX + KEY_USER] = request.user.username
setattr(response, "ak_context", {})
response.ak_context.update(LOCAL.authentik)
response.ak_context[KEY_USER] = request.user.username
for key in list(LOCAL.authentik.keys()):
del LOCAL.authentik[key]
return response

View File

@ -3,7 +3,6 @@
import uuid
from os import environ
import django.core.validators
import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
@ -12,6 +11,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.models import Count
import authentik.core.models
import authentik.lib.models
def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
@ -161,7 +161,7 @@ class Migration(migrations.Migration):
model_name="application",
name="meta_launch_url",
field=models.TextField(
blank=True, default="", validators=[django.core.validators.URLValidator()]
blank=True, default="", validators=[authentik.lib.models.DomainlessURLValidator()]
),
),
migrations.RunPython(

View File

@ -1,8 +1,9 @@
# Generated by Django 3.2.3 on 2021-06-02 21:51
import django.core.validators
from django.db import migrations, models
import authentik.lib.models
class Migration(migrations.Migration):
@ -17,7 +18,7 @@ class Migration(migrations.Migration):
field=models.TextField(
blank=True,
default="",
validators=[django.core.validators.URLValidator()],
validators=[authentik.lib.models.DomainlessURLValidator()],
),
),
]

View File

@ -9,7 +9,6 @@ from deepmerge import always_merger
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager as DjangoUserManager
from django.core import validators
from django.db import models
from django.db.models import Q, QuerySet, options
from django.http import HttpRequest
@ -29,7 +28,7 @@ from authentik.core.types import UILoginButton, UserSettingSerializer
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.generators import generate_id
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
from authentik.lib.models import CreatedUpdatedModel, DomainlessURLValidator, SerializerModel
from authentik.lib.utils.http import get_client_ip
from authentik.managed.models import ManagedModel
from authentik.policies.models import PolicyBindingModel
@ -174,7 +173,7 @@ class User(GuardianUserMixin, AbstractUser):
if mode == "none":
return DEFAULT_AVATAR
# gravatar uses md5 for their URLs, so md5 can't be avoided
mail_hash = md5(self.email.encode("utf-8")).hexdigest() # nosec
mail_hash = md5(self.email.lower().encode("utf-8")).hexdigest() # nosec
if mode == "gravatar":
parameters = [
("s", "158"),
@ -246,7 +245,7 @@ class Application(PolicyBindingModel):
)
meta_launch_url = models.TextField(
default="", blank=True, validators=[validators.URLValidator()]
default="", blank=True, validators=[DomainlessURLValidator()]
)
# For template applications, this can be set to /static/authentik/applications/*
meta_icon = models.FileField(

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block head %}
<script src="{% static 'dist/AdminInterface.js' %}" type="module"></script>
<script src="{% static 'dist/admin/AdminInterface.js' %}" type="module"></script>
{% endblock %}
{% block body %}

View File

@ -11,7 +11,7 @@
{% endblock %}
{% block head %}
<script src="{% static 'dist/FlowInterface.js' %}" type="module"></script>
<script src="{% static 'dist/flow/FlowInterface.js' %}" type="module"></script>
<style>
.pf-c-background-image::before {
--ak-flow-background: url("{{ flow.background_url }}");

View File

@ -4,7 +4,7 @@
{% load i18n %}
{% block head %}
<script src="{% static 'dist/UserInterface.js' %}" type="module"></script>
<script src="{% static 'dist/user/UserInterface.js' %}" type="module"></script>
{% endblock %}
{% block body %}

View File

@ -3,7 +3,8 @@ from django.urls import reverse
from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import Application, User
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
@ -12,7 +13,7 @@ class TestApplicationsAPI(APITestCase):
"""Test applications API"""
def setUp(self) -> None:
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.allowed = Application.objects.create(name="allowed", slug="allowed")
self.denied = Application.objects.create(name="denied", slug="denied")
PolicyBinding.objects.create(

View File

@ -6,6 +6,7 @@ from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
class TestAuthenticatedSessionsAPI(APITestCase):
@ -13,7 +14,7 @@ class TestAuthenticatedSessionsAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.other_user = User.objects.create(username="normal-user")
def test_list(self):

View File

@ -5,6 +5,7 @@ from django.test.testcases import TestCase
from django.urls import reverse
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
class TestImpersonation(TestCase):
@ -13,14 +14,14 @@ class TestImpersonation(TestCase):
def setUp(self) -> None:
super().setUp()
self.other_user = User.objects.create(username="to-impersonate")
self.akadmin = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
def test_impersonate_simple(self):
"""test simple impersonation and un-impersonation"""
# test with an inactive user to ensure that still works
self.other_user.is_active = False
self.other_user.save()
self.client.force_login(self.akadmin)
self.client.force_login(self.user)
self.client.get(
reverse(
@ -32,13 +33,13 @@ class TestImpersonation(TestCase):
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
self.assertEqual(response_body["user"]["username"], self.other_user.username)
self.assertEqual(response_body["original"]["username"], self.akadmin.username)
self.assertEqual(response_body["original"]["username"], self.user.username)
self.client.get(reverse("authentik_core:impersonate-end"))
response = self.client.get(reverse("authentik_api:user-me"))
response_body = loads(response.content.decode())
self.assertEqual(response_body["user"]["username"], self.akadmin.username)
self.assertEqual(response_body["user"]["username"], self.user.username)
self.assertNotIn("original", response_body)
def test_impersonate_denied(self):
@ -46,7 +47,7 @@ class TestImpersonation(TestCase):
self.client.force_login(self.other_user)
self.client.get(
reverse("authentik_core:impersonate-init", kwargs={"user_id": self.akadmin.pk})
reverse("authentik_core:impersonate-init", kwargs={"user_id": self.user.pk})
)
response = self.client.get(reverse("authentik_api:user-me"))

View File

@ -49,7 +49,7 @@ def provider_tester_factory(test_model: Type[Stage]) -> Callable:
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract:
if test_model._meta.abstract: # pragma: no cover
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
@ -59,6 +59,6 @@ def provider_tester_factory(test_model: Type[Stage]) -> Callable:
for model in all_subclasses(Source):
setattr(TestModels, f"test_model_{model.__name__}", source_tester_factory(model))
setattr(TestModels, f"test_source_{model.__name__}", source_tester_factory(model))
for model in all_subclasses(Provider):
setattr(TestModels, f"test_model_{model.__name__}", provider_tester_factory(model))
setattr(TestModels, f"test_provider_{model.__name__}", provider_tester_factory(model))

View File

@ -6,7 +6,8 @@ from rest_framework.serializers import ValidationError
from rest_framework.test import APITestCase
from authentik.core.api.propertymappings import PropertyMappingSerializer
from authentik.core.models import PropertyMapping, User
from authentik.core.models import PropertyMapping
from authentik.core.tests.utils import create_test_admin_user
class TestPropertyMappingAPI(APITestCase):
@ -17,7 +18,7 @@ class TestPropertyMappingAPI(APITestCase):
self.mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar'}"""
)
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_test_call(self):

View File

@ -2,7 +2,8 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import PropertyMapping, User
from authentik.core.models import PropertyMapping
from authentik.core.tests.utils import create_test_admin_user
class TestProvidersAPI(APITestCase):
@ -13,7 +14,7 @@ class TestProvidersAPI(APITestCase):
self.mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar'}"""
)
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_types(self):

View File

@ -8,6 +8,7 @@ from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents, User
from authentik.core.tasks import clean_expired_models
from authentik.core.tests.utils import create_test_admin_user
class TestTokenAPI(APITestCase):
@ -16,7 +17,7 @@ class TestTokenAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create(username="testuser")
self.admin = User.objects.get(username="akadmin")
self.admin = create_test_admin_user()
self.client.force_login(self.user)
def test_token_create(self):

View File

@ -3,7 +3,8 @@ from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_CHANGE_EMAIL, USER_ATTRIBUTE_CHANGE_USERNAME, User
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.models import FlowDesignation
from authentik.stages.email.models import EmailStage
from authentik.tenants.models import Tenant
@ -12,7 +13,7 @@ class TestUsersAPI(APITestCase):
"""Test Users API"""
def setUp(self) -> None:
self.admin = User.objects.get(username="akadmin")
self.admin = create_test_admin_user()
self.user = User.objects.create(username="test-user")
def test_update_self(self):
@ -69,10 +70,8 @@ class TestUsersAPI(APITestCase):
def test_recovery(self):
"""Test user recovery link (no recovery flow set)"""
flow = Flow.objects.create(
name="test", title="test", slug="test", designation=FlowDesignation.RECOVERY
)
tenant: Tenant = Tenant.objects.first()
flow = create_test_flow(FlowDesignation.RECOVERY)
tenant: Tenant = create_test_tenant()
tenant.flow_recovery = flow
tenant.save()
self.client.force_login(self.admin)
@ -99,10 +98,8 @@ class TestUsersAPI(APITestCase):
"""Test user recovery link (no email stage)"""
self.user.email = "foo@bar.baz"
self.user.save()
flow = Flow.objects.create(
name="test", title="test", slug="test", designation=FlowDesignation.RECOVERY
)
tenant: Tenant = Tenant.objects.first()
flow = create_test_flow(designation=FlowDesignation.RECOVERY)
tenant: Tenant = create_test_tenant()
tenant.flow_recovery = flow
tenant.save()
self.client.force_login(self.admin)
@ -115,10 +112,8 @@ class TestUsersAPI(APITestCase):
"""Test user recovery link"""
self.user.email = "foo@bar.baz"
self.user.save()
flow = Flow.objects.create(
name="test", title="test", slug="test", designation=FlowDesignation.RECOVERY
)
tenant: Tenant = Tenant.objects.first()
flow = create_test_flow(FlowDesignation.RECOVERY)
tenant: Tenant = create_test_tenant()
tenant.flow_recovery = flow
tenant.save()

View File

@ -0,0 +1,57 @@
"""Test Utils"""
from typing import Optional
from django.utils.text import slugify
from authentik.core.models import Group, User
from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow, FlowDesignation
from authentik.lib.generators import generate_id
from authentik.tenants.models import Tenant
def create_test_flow(designation: FlowDesignation = FlowDesignation.STAGE_CONFIGURATION) -> Flow:
"""Generate a flow that can be used for testing"""
uid = generate_id(10)
return Flow.objects.create(
name=uid,
title=uid,
slug=slugify(uid),
designation=designation,
)
def create_test_admin_user(name: Optional[str] = None) -> User:
"""Generate a test-admin user"""
uid = generate_id(20) if not name else name
group = Group.objects.create(name=uid, is_superuser=True)
user: User = User.objects.create(
username=uid,
name=uid,
email=f"{uid}@goauthentik.io",
)
user.set_password(uid)
user.save()
group.users.add(user)
return user
def create_test_tenant() -> Tenant:
"""Generate a test tenant, removing all other tenants to make sure this one
matches."""
uid = generate_id(20)
Tenant.objects.all().delete()
return Tenant.objects.create(domain=uid, default=True)
def create_test_cert() -> CertificateKeyPair:
"""Generate a certificate for testing"""
CertificateKeyPair.objects.filter(name="goauthentik.io").delete()
builder = CertificateBuilder()
builder.common_name = "goauthentik.io"
builder.build(
subject_alt_names=["goauthentik.io"],
validity_days=360,
)
return builder.save()

View File

@ -55,7 +55,7 @@ class CertificateKeyPair(ManagedModel, CreatedUpdatedModel):
@property
def private_key(self) -> Optional[RSAPrivateKey]:
"""Get python cryptography PrivateKey instance"""
if not self._private_key and self._private_key != "":
if not self._private_key and self.key_data != "":
try:
self._private_key = load_pem_private_key(
str.encode("\n".join([x.strip() for x in self.key_data.split("\n")])),

View File

@ -1,25 +1,33 @@
"""Crypto tests"""
import datetime
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.api.used_by import DeleteAction
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.api import CertificateKeyPairSerializer
from authentik.crypto.builder import CertificateBuilder
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.lib.generators import generate_key
from authentik.providers.oauth2.models import OAuth2Provider
class TestCrypto(TestCase):
class TestCrypto(APITestCase):
"""Test Crypto validation"""
def test_model_private(self):
"""Test model private key"""
cert = CertificateKeyPair.objects.create(
name="test",
certificate_data="foo",
key_data="foo",
)
self.assertIsNone(cert.private_key)
def test_serializer(self):
"""Test API Validation"""
keypair = CertificateKeyPair.objects.first()
keypair = create_test_cert()
self.assertTrue(
CertificateKeyPairSerializer(
data={
@ -54,10 +62,38 @@ class TestCrypto(TestCase):
self.assertEqual(instance.name, "test-cert")
self.assertEqual((instance.certificate.not_valid_after - now).days, 2)
def test_builder_api(self):
"""Test Builder (via API)"""
self.client.force_login(create_test_admin_user())
self.client.post(
reverse("authentik_api:certificatekeypair-generate"),
data={"common_name": "foo", "subject_alt_name": "bar,baz", "validity_days": 3},
)
self.assertTrue(CertificateKeyPair.objects.filter(name="foo").exists())
def test_builder_api_invalid(self):
"""Test Builder (via API) (invalid)"""
self.client.force_login(create_test_admin_user())
response = self.client.post(
reverse("authentik_api:certificatekeypair-generate"),
data={},
)
self.assertEqual(response.status_code, 400)
def test_list(self):
"""Test API List"""
self.client.force_login(create_test_admin_user())
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-list",
)
)
self.assertEqual(200, response.status_code)
def test_certificate_download(self):
"""Test certificate export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
self.client.force_login(create_test_admin_user())
keypair = create_test_cert()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
@ -77,8 +113,8 @@ class TestCrypto(TestCase):
def test_private_key_download(self):
"""Test private_key export (download)"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
self.client.force_login(create_test_admin_user())
keypair = create_test_cert()
response = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-private-key",
@ -98,15 +134,15 @@ class TestCrypto(TestCase):
def test_used_by(self):
"""Test used_by endpoint"""
self.client.force_login(User.objects.get(username="akadmin"))
keypair = CertificateKeyPair.objects.first()
self.client.force_login(create_test_admin_user())
keypair = create_test_cert()
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://localhost",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=keypair,
)
response = self.client.get(
reverse(

View File

@ -4,7 +4,6 @@ import uuid
from datetime import timedelta
from typing import Iterable
import django.core.validators
import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
@ -12,6 +11,7 @@ from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.events.models
import authentik.lib.models
from authentik.events.models import EventAction, NotificationSeverity, TransportMode
@ -826,6 +826,8 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="notificationtransport",
name="webhook_url",
field=models.TextField(blank=True, validators=[django.core.validators.URLValidator()]),
field=models.TextField(
blank=True, validators=[authentik.lib.models.DomainlessURLValidator()]
),
),
]

View File

@ -1,8 +1,9 @@
# Generated by Django 3.2.7 on 2021-10-04 15:31
import django.core.validators
from django.db import migrations, models
import authentik.lib.models
class Migration(migrations.Migration):
@ -14,6 +15,8 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="notificationtransport",
name="webhook_url",
field=models.TextField(blank=True, validators=[django.core.validators.URLValidator()]),
field=models.TextField(
blank=True, validators=[authentik.lib.models.DomainlessURLValidator()]
),
),
]

View File

@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Optional, Type, Union
from uuid import uuid4
from django.conf import settings
from django.core.validators import URLValidator
from django.db import models
from django.http import HttpRequest
from django.http.request import QueryDict
@ -20,6 +19,7 @@ from authentik.core.middleware import SESSION_IMPERSONATE_ORIGINAL_USER, SESSION
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.lib.models import DomainlessURLValidator
from authentik.lib.sentry import SentryIgnoredException
from authentik.lib.utils.http import get_client_ip, get_http_session
from authentik.lib.utils.time import timedelta_from_string
@ -224,7 +224,7 @@ class NotificationTransport(models.Model):
name = models.TextField(unique=True)
mode = models.TextField(choices=TransportMode.choices)
webhook_url = models.TextField(blank=True, validators=[URLValidator()])
webhook_url = models.TextField(blank=True, validators=[DomainlessURLValidator()])
webhook_mapping = models.ForeignKey(
"NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None
)

View File

@ -3,14 +3,14 @@ from threading import Thread
from typing import Any, Optional
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
from django.db.models.signals import post_save
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.http import HttpRequest
from authentik.core.models import User
from authentik.core.signals import password_changed
from authentik.events.models import Event, EventAction
from authentik.events.tasks import event_notification_handler
from authentik.events.tasks import event_notification_handler, gdpr_cleanup
from authentik.flows.planner import PLAN_CONTEXT_SOURCE, FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.stages.invitation.models import Invitation
@ -108,3 +108,10 @@ def on_password_changed(sender, user: User, password: str, **_):
def event_post_save_notification(sender, instance: Event, **_):
"""Start task to check if any policies trigger an notification on this event"""
event_notification_handler.delay(instance.event_uuid.hex)
@receiver(pre_delete, sender=User)
# pylint: disable=unused-argument
def event_user_pre_delete_cleanup(sender, instance: User, **_):
"""If gdpr_compliance is enabled, remove all the user's events"""
gdpr_cleanup.delay(instance.pk)

View File

@ -106,3 +106,11 @@ def notification_transport(self: MonitoredTask, notification_pk: int, transport_
except NotificationTransportError as exc:
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
raise exc
@CELERY_APP.task()
def gdpr_cleanup(user_pk: int):
"""cleanup events from gdpr_compliance"""
events = Event.objects.filter(user__pk=user_pk)
LOGGER.debug("GDPR cleanup, removing events from user", events=events.count())
events.delete()

View File

@ -3,7 +3,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import (
Event,
EventAction,
@ -17,7 +17,7 @@ class TestEventsAPI(APITestCase):
"""Test Event API"""
def setUp(self) -> None:
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_top_n(self):

View File

@ -3,7 +3,8 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import Application, User
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user
from authentik.events.models import Event, EventAction
@ -12,7 +13,7 @@ class TestEventsMiddleware(APITestCase):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_create(self):

View File

@ -41,6 +41,7 @@ class StageSerializer(ModelSerializer, MetaNameSerializer):
"component",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
"flow_set",
]

View File

@ -10,7 +10,7 @@ from django.test import RequestFactory
from structlog.stdlib import get_logger
from authentik import __version__
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
@ -68,7 +68,7 @@ class Command(BaseCommand): # pragma: no cover
def benchmark_flows(self, proc_count):
"""Get full recovery link"""
flow = Flow.objects.get(slug="default-authentication-flow")
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
manager = Manager()
return_dict = manager.dict()

View File

@ -149,7 +149,7 @@ class ChallengeStageView(StageView):
)
challenge_response.initial_data["response_errors"] = full_errors
if not challenge_response.is_valid():
LOGGER.warning(
LOGGER.error(
"f(ch): invalid challenge response",
binding=self.executor.current_binding,
errors=challenge_response.errors,

View File

@ -2,7 +2,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.api.stages import StageSerializer, StageViewSet
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage
from authentik.policies.dummy.models import DummyPolicy
@ -47,7 +47,7 @@ class TestFlowsAPI(APITestCase):
def test_api_diagram(self):
"""Test flow diagram."""
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
self.client.force_login(user)
flow = Flow.objects.create(
@ -77,7 +77,7 @@ class TestFlowsAPI(APITestCase):
def test_api_diagram_no_stages(self):
"""Test flow diagram with no stages."""
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
self.client.force_login(user)
flow = Flow.objects.create(
@ -93,7 +93,7 @@ class TestFlowsAPI(APITestCase):
def test_types(self):
"""Test Stage's types endpoint"""
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
self.client.force_login(user)
response = self.client.get(

View File

@ -6,7 +6,7 @@ from django.test.client import RequestFactory
from django.urls.base import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, InvalidResponseAction
from authentik.stages.dummy.models import DummyStage
@ -18,7 +18,7 @@ class TestFlowInspector(APITestCase):
def setUp(self):
self.request_factory = RequestFactory()
self.admin = User.objects.get(username="akadmin")
self.admin = create_test_admin_user()
self.client.force_login(self.admin)
def test(self):
@ -77,7 +77,7 @@ class TestFlowInspector(APITestCase):
self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
{"uid_field": "akadmin"},
{"uid_field": self.admin.username},
follow=True,
)
@ -89,5 +89,5 @@ class TestFlowInspector(APITestCase):
self.assertEqual(content["plans"][0]["current_stage"]["stage_obj"]["name"], "ident")
self.assertEqual(content["current_plan"]["current_stage"]["stage_obj"]["name"], "dummy2")
self.assertEqual(
content["current_plan"]["plan_context"]["pending_user"]["username"], "akadmin"
content["current_plan"]["plan_context"]["pending_user"]["username"], self.admin.username
)

View File

@ -17,13 +17,13 @@ def model_tester_factory(test_model: Type[Stage]) -> Callable:
def tester(self: TestModels):
model_class = None
if test_model._meta.abstract:
if test_model._meta.abstract: # pragma: no cover
model_class = test_model.__bases__[0]()
else:
model_class = test_model()
self.assertTrue(issubclass(model_class.type, StageView))
self.assertIsNotNone(test_model.component)
_ = test_model.ui_user_settings
_ = model_class.ui_user_settings
return tester

View File

@ -2,6 +2,7 @@
from django.test import TestCase
from django.urls import reverse
from authentik.core.tests.utils import create_test_flow
from authentik.flows.models import Flow, FlowDesignation
from authentik.flows.planner import FlowPlan
from authentik.flows.views.executor import SESSION_KEY_PLAN
@ -12,9 +13,8 @@ class TestHelperView(TestCase):
def test_default_view(self):
"""Test that ToDefaultFlow returns the expected URL"""
flow = Flow.objects.filter(
designation=FlowDesignation.INVALIDATION,
).first()
Flow.objects.filter(designation=FlowDesignation.INVALIDATION).delete()
flow = create_test_flow(FlowDesignation.INVALIDATION)
response = self.client.get(
reverse("authentik_flows:default-invalidation"),
)
@ -24,9 +24,8 @@ class TestHelperView(TestCase):
def test_default_view_invalid_plan(self):
"""Test that ToDefaultFlow returns the expected URL (with an invalid plan)"""
flow = Flow.objects.filter(
designation=FlowDesignation.INVALIDATION,
).first()
Flow.objects.filter(designation=FlowDesignation.INVALIDATION).delete()
flow = create_test_flow(FlowDesignation.INVALIDATION)
plan = FlowPlan(flow_pk=flow.pk.hex + "aa")
session = self.client.session
session[SESSION_KEY_PLAN] = plan

View File

@ -3,7 +3,9 @@ import os
from collections.abc import Mapping
from contextlib import contextmanager
from glob import glob
from json import dumps
from json import dumps, loads
from json.decoder import JSONDecodeError
from sys import argv, stderr
from time import time
from typing import Any
from urllib.parse import urlparse
@ -59,7 +61,7 @@ class ConfigLoader:
"timestamp": time(),
}
output.update(kwargs)
print(dumps(output))
print(dumps(output), file=stderr)
def update(self, root: dict[str, Any], updatee: dict[str, Any]) -> dict[str, Any]:
"""Recursively update dictionary"""
@ -81,8 +83,8 @@ class ConfigLoader:
try:
with open(url.path, "r", encoding="utf8") as _file:
value = _file.read()
except OSError:
self._log("error", f"Failed to read config value from {url.path}")
except OSError as exc:
self._log("error", f"Failed to read config value from {url.path}: {exc}")
value = url.query
return value
@ -123,6 +125,11 @@ class ConfigLoader:
if dot_part not in current_obj:
current_obj[dot_part] = {}
current_obj = current_obj[dot_part]
# Check if the value is json, and try to load it
try:
value = loads(value)
except JSONDecodeError:
pass
current_obj[dot_parts[-1]] = value
idx += 1
if idx > 0:
@ -174,3 +181,9 @@ class ConfigLoader:
CONFIG = ConfigLoader()
if __name__ == "__main__":
if len(argv) < 2:
print(dumps(CONFIG.raw, indent=4))
else:
print(CONFIG.y(argv[1]))

View File

@ -64,7 +64,7 @@ outposts:
# %(type)s: Outpost type; proxy, ldap, etc
# %(version)s: Current version; 2021.4.1
# %(build_hash)s: Build hash if you're running a beta version
container_image_base: env://AUTHENTIK_OUTPOSTS__DOCKER_IMAGE_BASE?goauthentik.io/%(type)s:%(version)s
container_image_base: goauthentik.io/%(type)s:%(version)s
cookie_domain: null
disable_update_check: false
@ -72,9 +72,13 @@ disable_startup_analytics: false
avatars: env://AUTHENTIK_AUTHENTIK__AVATARS?gravatar
geoip: "./GeoLite2-City.mmdb"
# Can't currently be configured via environment variables, only yaml
footer_links:
- name: Documentation
href: https://goauthentik.io/docs/?utm_source=authentik
- name: authentik Website
href: https://goauthentik.io/?utm_source=authentik
default_user_change_email: true
default_user_change_username: true
gdpr_compliance: true

View File

@ -66,3 +66,11 @@ class DomainlessURLValidator(URLValidator):
r"\Z",
re.IGNORECASE,
)
self.schemes = ["http", "https", "blank"] + list(self.schemes)
def __call__(self, value):
# Check if the scheme is valid.
scheme = value.split("://")[0].lower()
if scheme not in self.schemes:
value = "default" + value
return super().__call__(value)

View File

@ -8,6 +8,7 @@ from botocore.exceptions import BotoCoreError
from celery.exceptions import CeleryError
from channels.middleware import BaseMiddleware
from channels_redis.core import ChannelFull
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation, ValidationError
from django.db import InternalError, OperationalError, ProgrammingError
from django.http.response import Http404
@ -92,6 +93,7 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
# End-user errors
Http404,
)
exc_value = None
if "exc_info" in hint:
_, exc_value, _ = hint["exc_info"]
if isinstance(exc_value, ignored_classes):
@ -105,6 +107,10 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
"asyncio",
"multiprocessing",
"django_redis",
"django.security.DisallowedHost",
]:
return None
LOGGER.debug("sending event to sentry", exc=exc_value, source_logger=event.get("logger", None))
if settings.DEBUG:
return None
return event

View File

@ -1,7 +1,7 @@
"""Test Evaluator base functions"""
from django.test import TestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.expression.evaluator import BaseEvaluator
@ -19,12 +19,11 @@ class TestEvaluator(TestCase):
def test_user_by(self):
"""Test expr_user_by"""
self.assertIsNotNone(BaseEvaluator.expr_user_by(username="akadmin"))
user = create_test_admin_user()
self.assertIsNotNone(BaseEvaluator.expr_user_by(username=user.username))
self.assertIsNone(BaseEvaluator.expr_user_by(username="bar"))
self.assertIsNone(BaseEvaluator.expr_user_by(foo="bar"))
def test_is_group_member(self):
"""Test expr_is_group_member"""
self.assertFalse(
BaseEvaluator.expr_is_group_member(User.objects.get(username="akadmin"), name="test")
)
self.assertFalse(BaseEvaluator.expr_is_group_member(create_test_admin_user(), name="test"))

View File

@ -1,17 +1,24 @@
"""Test HTTP Helpers"""
from django.test import RequestFactory, TestCase
from authentik.core.models import USER_ATTRIBUTE_CAN_OVERRIDE_IP, Token, TokenIntents, User
from authentik.core.models import USER_ATTRIBUTE_CAN_OVERRIDE_IP, Token, TokenIntents
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.utils.http import OUTPOST_REMOTE_IP_HEADER, OUTPOST_TOKEN_HEADER, get_client_ip
from authentik.lib.views import bad_request_message
class TestHTTP(TestCase):
"""Test HTTP Helpers"""
def setUp(self) -> None:
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.factory = RequestFactory()
def test_bad_request_message(self):
"""test bad_request_message"""
request = self.factory.get("/")
self.assertEqual(bad_request_message(request, "foo").status_code, 400)
def test_normal(self):
"""Test normal request"""
request = self.factory.get("/")

View File

@ -20,5 +20,5 @@ def managed_reconcile(self: MonitoredTask):
self.set_status(
TaskResult(TaskResultStatus.SUCCESSFUL, ["Successfully updated managed models."])
)
except DatabaseError as exc:
except DatabaseError as exc: # pragma: no cover
self.set_status(TaskResult(TaskResultStatus.WARNING, [str(exc)]))

View File

@ -0,0 +1,13 @@
"""managed tests"""
from django.test import TestCase
from authentik.managed.tasks import managed_reconcile
class TestManaged(TestCase):
"""managed tests"""
def test_reconcile(self):
"""Test reconcile"""
# pyright: reportGeneralTypeIssues=false
managed_reconcile() # pylint: disable=no-value-for-parameter

View File

@ -46,6 +46,7 @@ class ServiceConnectionSerializer(ModelSerializer, MetaNameSerializer):
"component",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
]

View File

@ -126,7 +126,7 @@ class OutpostConsumer(AuthJsonConsumer):
self.send_json(asdict(response))
# pylint: disable=unused-argument
def event_update(self, event):
def event_update(self, event): # pragma: no cover
"""Event handler which is called by post_save signals, Send update instruction"""
self.send_json(
asdict(WebsocketMessage(instruction=WebsocketMessageInstruction.TRIGGER_UPDATE))

View File

@ -67,8 +67,6 @@ class OutpostConfig:
authentik_host_browser: str = ""
log_level: str = CONFIG.y("log_level")
error_reporting_enabled: bool = CONFIG.y_bool("error_reporting.enabled")
error_reporting_environment: str = CONFIG.y("error_reporting.environment", "customer")
object_naming_template: str = field(default="ak-outpost-%(name)s")
docker_network: Optional[str] = field(default=None)

View File

@ -2,8 +2,8 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import PropertyMapping, User
from authentik.flows.models import Flow
from authentik.core.models import PropertyMapping
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.outposts.api.outposts import OutpostSerializer
from authentik.outposts.models import OutpostType, default_outpost_config
from authentik.providers.ldap.models import LDAPProvider
@ -18,7 +18,7 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
self.mapping = PropertyMapping.objects.create(
name="dummy", expression="""return {'foo': 'bar'}"""
)
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_outpost_validaton(self):
@ -30,7 +30,7 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
"config": default_outpost_config(),
"providers": [
ProxyProvider.objects.create(
name="test", authorization_flow=Flow.objects.first()
name="test", authorization_flow=create_test_flow()
).pk
],
}
@ -43,7 +43,7 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
"config": default_outpost_config(),
"providers": [
LDAPProvider.objects.create(
name="test", authorization_flow=Flow.objects.first()
name="test", authorization_flow=create_test_flow()
).pk
],
}
@ -60,9 +60,7 @@ class TestOutpostServiceConnectionsAPI(APITestCase):
def test_outpost_config(self):
"""Test Outpost's config field"""
provider = ProxyProvider.objects.create(
name="test", authorization_flow=Flow.objects.first()
)
provider = ProxyProvider.objects.create(name="test", authorization_flow=create_test_flow())
invalid = OutpostSerializer(data={"name": "foo", "providers": [provider.pk], "config": ""})
self.assertFalse(invalid.is_valid())
self.assertIn("config", invalid.errors)

View File

@ -0,0 +1,15 @@
"""management command tests"""
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
class TestManagementCommands(TestCase):
"""management command tests"""
def test_repair_permissions(self):
"""Test repair_permissions"""
out = StringIO()
call_command("repair_permissions", stdout=out)
self.assertNotEqual(out.getvalue(), "")

View File

@ -4,8 +4,7 @@ from django.contrib.auth.management import create_permissions
from django.test import TestCase
from guardian.models import UserObjectPermission
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.proxy.models import ProxyProvider
@ -23,7 +22,7 @@ class OutpostTests(TestCase):
name="test",
internal_host="http://localhost",
external_host="http://localhost",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
)
outpost: Outpost = Outpost.objects.create(
name="test",
@ -45,7 +44,7 @@ class OutpostTests(TestCase):
self.assertEqual(permissions[1].object_pk, str(provider.pk))
# Provider requires a certificate-key-pair, user should have permissions for it
keypair = CertificateKeyPair.objects.first()
keypair = create_test_cert()
provider.certificate = keypair
provider.save()
permissions = UserObjectPermission.objects.filter(user=outpost.user).order_by(

View File

@ -0,0 +1,97 @@
"""Websocket tests"""
from dataclasses import asdict
from channels.routing import URLRouter
from channels.testing import WebsocketCommunicator
from django.test import TransactionTestCase
from authentik import __version__
from authentik.flows.models import Flow, FlowDesignation
from authentik.outposts.channels import WebsocketMessage, WebsocketMessageInstruction
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.proxy.models import ProxyProvider
from authentik.root import websocket
class TestOutpostWS(TransactionTestCase):
"""Websocket tests"""
def setUp(self) -> None:
self.provider: ProxyProvider = ProxyProvider.objects.create(
name="test",
internal_host="http://localhost",
external_host="http://localhost",
authorization_flow=Flow.objects.create(
name="foo", slug="foo", designation=FlowDesignation.AUTHORIZATION
),
)
self.outpost: Outpost = Outpost.objects.create(
name="test",
type=OutpostType.PROXY,
)
self.outpost.providers.add(self.provider)
self.token = self.outpost.token.key
async def test_auth(self):
"""Test auth without token"""
communicator = WebsocketCommunicator(
URLRouter(websocket.websocket_urlpatterns), f"/ws/outpost/{self.outpost.pk}/"
)
connected, _ = await communicator.connect()
self.assertFalse(connected)
async def test_auth_valid(self):
"""Test auth with token"""
communicator = WebsocketCommunicator(
URLRouter(websocket.websocket_urlpatterns),
f"/ws/outpost/{self.outpost.pk}/",
{b"authorization": f"Bearer {self.token}".encode()},
)
connected, _ = await communicator.connect()
self.assertTrue(connected)
async def test_send(self):
"""Test sending of Hello"""
communicator = WebsocketCommunicator(
URLRouter(websocket.websocket_urlpatterns),
f"/ws/outpost/{self.outpost.pk}/",
{b"authorization": f"Bearer {self.token}".encode()},
)
connected, _ = await communicator.connect()
self.assertTrue(connected)
await communicator.send_json_to(
asdict(
WebsocketMessage(
instruction=WebsocketMessageInstruction.HELLO,
args={
"version": __version__,
"buildHash": "foo",
"uuid": "123",
},
)
)
)
response = await communicator.receive_json_from()
self.assertEqual(
response, asdict(WebsocketMessage(instruction=WebsocketMessageInstruction.ACK, args={}))
)
await communicator.disconnect()
async def test_send_ack(self):
"""Test sending of ACK"""
communicator = WebsocketCommunicator(
URLRouter(websocket.websocket_urlpatterns),
f"/ws/outpost/{self.outpost.pk}/",
{b"authorization": f"Bearer {self.token}".encode()},
)
connected, _ = await communicator.connect()
self.assertTrue(connected)
await communicator.send_json_to(
asdict(
WebsocketMessage(
instruction=WebsocketMessageInstruction.ACK,
args={},
)
)
)
await communicator.disconnect()

View File

@ -66,6 +66,7 @@ class PolicySerializer(ModelSerializer, MetaNameSerializer):
"component",
"verbose_name",
"verbose_name_plural",
"meta_model_name",
"bound_to",
]
depth = 3

View File

@ -50,7 +50,7 @@ class PolicyEvaluator(BaseEvaluator):
if device_class == device_type:
return True
return False
return len(user_devices) > 0
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)"""

View File

@ -26,7 +26,7 @@ class TestHIBPPolicy(TestCase):
name="test_false",
)
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "password"
request.context["password"] = "password" # nosec
result: PolicyResult = policy.passes(request)
self.assertFalse(result.passing)
self.assertTrue(result.messages[0].startswith("Password exists on "))

View File

@ -30,7 +30,7 @@ class TestPasswordPolicy(TestCase):
def test_failed_length(self):
"""Password too short"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "test"
request.context["password"] = "test" # nosec
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
@ -38,7 +38,7 @@ class TestPasswordPolicy(TestCase):
def test_failed_lowercase(self):
"""not enough lowercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "TTTTTTTTTTTTTTTTTTTTTTTe"
request.context["password"] = "TTTTTTTTTTTTTTTTTTTTTTTe" # nosec
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
@ -46,7 +46,7 @@ class TestPasswordPolicy(TestCase):
def test_failed_uppercase(self):
"""not enough uppercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "tttttttttttttttttttttttE"
request.context["password"] = "tttttttttttttttttttttttE" # nosec
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
@ -54,7 +54,7 @@ class TestPasswordPolicy(TestCase):
def test_failed_symbols(self):
"""not enough uppercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "TETETETETETETETETETETETETe!!!"
request.context["password"] = "TETETETETETETETETETETETETe!!!" # nosec
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
@ -62,7 +62,7 @@ class TestPasswordPolicy(TestCase):
def test_true(self):
"""Positive password case"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = generate_key() + "ee!!!"
request.context["password"] = generate_key() + "ee!!!" # nosec
result: PolicyResult = self.policy.passes(request)
self.assertTrue(result.passing)
self.assertEqual(result.messages, tuple())

View File

@ -2,7 +2,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.policies.models import PolicyBindingModel
@ -12,8 +12,8 @@ class TestBindingsAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.pbm = PolicyBindingModel.objects.create()
self.group = Group.objects.first()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.group = self.user.ak_groups.first()
self.client.force_login(self.user)
def test_valid_binding(self):

View File

@ -2,7 +2,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.policies.dummy.models import DummyPolicy
@ -12,7 +12,7 @@ class TestPoliciesAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.policy = DummyPolicy.objects.create(name="dummy", result=True)
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_test_call(self):

View File

@ -2,8 +2,7 @@
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.flows.models import Flow, FlowDesignation
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.providers.oauth2.models import JWTAlgorithms
@ -12,7 +11,7 @@ class TestOAuth2ProviderAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_validate(self):
@ -24,9 +23,7 @@ class TestOAuth2ProviderAPI(APITestCase):
data={
"name": "test",
"jwt_alg": str(JWTAlgorithms.RS256),
"authorization_flow": Flow.objects.filter(designation=FlowDesignation.AUTHORIZATION)
.first()
.pk,
"authorization_flow": create_test_flow().pk,
},
)
self.assertJSONEqual(

View File

@ -3,8 +3,8 @@ from django.test import RequestFactory
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application, User
from authentik.crypto.models import CertificateKeyPair
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id, generate_key
@ -43,7 +43,7 @@ class TestAuthorize(OAuthTestCase):
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
)
with self.assertRaises(AuthorizeError):
@ -63,7 +63,7 @@ class TestAuthorize(OAuthTestCase):
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
)
with self.assertRaises(RedirectUriError):
@ -85,7 +85,7 @@ class TestAuthorize(OAuthTestCase):
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
)
with self.assertRaises(RedirectUriError):
request = self.factory.get("/", data={"response_type": "code", "client_id": "test"})
@ -105,7 +105,7 @@ class TestAuthorize(OAuthTestCase):
OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
)
request = self.factory.get(
@ -184,7 +184,7 @@ class TestAuthorize(OAuthTestCase):
)
Application.objects.create(name="app", slug="app", provider=provider)
state = generate_id()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
self.client.force_login(user)
# Step 1, initiate params and get redirect to flow
self.client.get(
@ -218,11 +218,11 @@ class TestAuthorize(OAuthTestCase):
client_secret=generate_key(),
authorization_flow=flow,
redirect_uris="http://localhost",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
Application.objects.create(name="app", slug="app", provider=provider)
state = generate_id()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
self.client.force_login(user)
# Step 1, initiate params and get redirect to flow
self.client.get(

View File

@ -6,8 +6,7 @@ from django.urls.base import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.providers.oauth2.models import OAuth2Provider
from authentik.providers.oauth2.tests.utils import OAuthTestCase
@ -24,9 +23,9 @@ class TestJWKS(OAuthTestCase):
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
app = Application.objects.create(name="test", slug="test", provider=provider)
response = self.client.get(
@ -40,7 +39,7 @@ class TestJWKS(OAuthTestCase):
provider = OAuth2Provider.objects.create(
name="test",
client_id="test",
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
)
app = Application.objects.create(name="test", slug="test", provider=provider)

View File

@ -5,10 +5,9 @@ from django.test import RequestFactory
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application, User
from authentik.crypto.models import CertificateKeyPair
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id, generate_key
from authentik.providers.oauth2.constants import (
GRANT_TYPE_AUTHORIZATION_CODE,
@ -34,12 +33,12 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://testserver",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
code = AuthorizationCode.objects.create(code="foobar", provider=provider, user=user)
request = self.factory.post(
"/",
@ -61,9 +60,9 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://testserver",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
request = self.factory.post(
@ -84,12 +83,12 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,
@ -113,15 +112,15 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
# Needs to be assigned to an application for iss to be set
self.app.provider = provider
self.app.save()
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
code = AuthorizationCode.objects.create(
code="foobar", provider=provider, user=user, is_open_id=True
)
@ -155,15 +154,15 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
# Needs to be assigned to an application for iss to be set
self.app.provider = provider
self.app.save()
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,
@ -204,12 +203,12 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://local.invalid",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,
@ -249,15 +248,15 @@ class TestToken(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="http://testserver",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
# Needs to be assigned to an application for iss to be set
self.app.provider = provider
self.app.save()
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
user = User.objects.get(username="akadmin")
user = create_test_admin_user()
token: RefreshToken = RefreshToken.objects.create(
provider=provider,
user=user,

View File

@ -5,10 +5,9 @@ from dataclasses import asdict
from django.urls import reverse
from django.utils.encoding import force_str
from authentik.core.models import Application, User
from authentik.crypto.models import CertificateKeyPair
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.providers.oauth2.models import IDToken, OAuth2Provider, RefreshToken, ScopeMapping
@ -26,15 +25,15 @@ class TestUserinfo(OAuthTestCase):
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=Flow.objects.first(),
authorization_flow=create_test_flow(),
redirect_uris="",
rsa_key=CertificateKeyPair.objects.first(),
rsa_key=create_test_cert(),
)
self.provider.property_mappings.set(ScopeMapping.objects.all())
# Needs to be assigned to an application for iss to be set
self.app.provider = self.provider
self.app.save()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.token: RefreshToken = RefreshToken.objects.create(
provider=self.provider,
user=self.user,
@ -57,12 +56,12 @@ class TestUserinfo(OAuthTestCase):
self.assertJSONEqual(
force_str(res.content),
{
"name": "authentik Default Admin",
"given_name": "authentik Default Admin",
"name": self.user.name,
"given_name": self.user.name,
"family_name": "",
"preferred_username": "akadmin",
"nickname": "akadmin",
"groups": ["authentik Admins"],
"preferred_username": self.user.name,
"nickname": self.user.name,
"groups": [group.name for group in self.user.ak_groups.all()],
"sub": "bar",
},
)
@ -80,12 +79,12 @@ class TestUserinfo(OAuthTestCase):
self.assertJSONEqual(
force_str(res.content),
{
"name": "authentik Default Admin",
"given_name": "authentik Default Admin",
"name": self.user.name,
"given_name": self.user.name,
"family_name": "",
"preferred_username": "akadmin",
"nickname": "akadmin",
"groups": ["authentik Admins"],
"preferred_username": self.user.name,
"nickname": self.user.name,
"groups": [group.name for group in self.user.ak_groups.all()],
"sub": "bar",
},
)

View File

@ -369,7 +369,7 @@ class OAuthFulfillmentStage(StageView):
if self.params.grant_type == GrantTypes.HYBRID:
query_fragment["code"] = code.code
query_fragment["token_type"] = "bearer"
query_fragment["token_type"] = "bearer" # nosec
query_fragment["expires_in"] = int(
timedelta_from_string(self.provider.access_code_validity).total_seconds()
)

View File

@ -11,6 +11,7 @@ from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import PassiveSerializer
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.models import ScopeMapping
from authentik.providers.oauth2.views.provider import ProviderInfoView
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
@ -110,6 +111,7 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
oidc_configuration = SerializerMethodField()
token_validity = SerializerMethodField()
scopes_to_request = SerializerMethodField()
@extend_schema_field(OpenIDConnectConfigurationSerializer)
def get_oidc_configuration(self, obj: ProxyProvider):
@ -120,6 +122,14 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
"""Get token validity as second count"""
return timedelta_from_string(obj.token_validity).total_seconds()
def get_scopes_to_request(self, obj: ProxyProvider) -> list[str]:
"""Get all the scope names the outpost should request,
including custom-defined ones"""
scope_names = set(
ScopeMapping.objects.filter(provider__in=[obj]).values_list("scope_name", flat=True)
)
return list(scope_names)
class Meta:
model = ProxyProvider
@ -141,6 +151,7 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
"mode",
"cookie_domain",
"token_validity",
"scopes_to_request",
]

View File

@ -110,14 +110,14 @@ class Migration(migrations.Migration):
"require_signing",
models.BooleanField(
default=False,
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Singing Keypair`.",
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Signing Keypair`.",
),
),
(
"signing_kp",
models.ForeignKey(
default=None,
help_text="Singing is enabled upon selection of a Key Pair.",
help_text="Signing is enabled upon selection of a Key Pair.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_crypto.CertificateKeyPair",

View File

@ -114,14 +114,14 @@ class Migration(migrations.Migration):
"require_signing",
models.BooleanField(
default=False,
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Singing Keypair`.",
help_text="Require Requests to be signed by an X509 Certificate. Must match the Certificate selected in `Signing Keypair`.",
),
),
(
"signing_kp",
models.ForeignKey(
default=None,
help_text="Singing is enabled upon selection of a Key Pair.",
help_text="Signing is enabled upon selection of a Key Pair.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_crypto.certificatekeypair",

View File

@ -45,6 +45,7 @@ class AssertionProcessor:
_assertion_id: str
_valid_not_before: str
_session_not_on_or_after: str
_valid_not_on_or_after: str
def __init__(self, provider: SAMLProvider, request: HttpRequest, auth_n_request: AuthNRequest):
@ -58,6 +59,9 @@ class AssertionProcessor:
self._valid_not_before = get_time_string(
timedelta_from_string(self.provider.assertion_valid_not_before)
)
self._session_not_on_or_after = get_time_string(
timedelta_from_string(self.provider.session_valid_not_on_or_after)
)
self._valid_not_on_or_after = get_time_string(
timedelta_from_string(self.provider.assertion_valid_not_on_or_after)
)
@ -117,6 +121,7 @@ class AssertionProcessor:
auth_n_statement = Element(f"{{{NS_SAML_ASSERTION}}}AuthnStatement")
auth_n_statement.attrib["AuthnInstant"] = self._valid_not_before
auth_n_statement.attrib["SessionIndex"] = self._assertion_id
auth_n_statement.attrib["SessionNotOnOrAfter"] = self._session_not_on_or_after
auth_n_context = SubElement(auth_n_statement, f"{{{NS_SAML_ASSERTION}}}AuthnContext")
auth_n_context_class_ref = SubElement(

View File

@ -36,7 +36,7 @@ class MetadataProcessor:
self.xml_id = get_random_id()
def get_signing_key_descriptor(self) -> Optional[Element]:
"""Get Singing KeyDescriptor, if enabled for the provider"""
"""Get Signing KeyDescriptor, if enabled for the provider"""
if not self.provider.signing_kp:
return None
key_descriptor = Element(f"{{{NS_SAML_METADATA}}}KeyDescriptor")

View File

@ -4,8 +4,9 @@ from tempfile import TemporaryFile
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import Application, User
from authentik.flows.models import Flow, FlowDesignation
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.flows.models import FlowDesignation
from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.tests.test_metadata import METADATA_SIMPLE
@ -15,7 +16,7 @@ class TestSAMLProviderAPI(APITestCase):
def setUp(self) -> None:
super().setUp()
self.user = User.objects.get(username="akadmin")
self.user = create_test_admin_user()
self.client.force_login(self.user)
def test_metadata(self):
@ -23,9 +24,7 @@ class TestSAMLProviderAPI(APITestCase):
self.client.logout()
provider = SAMLProvider.objects.create(
name="test",
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
)
Application.objects.create(name="test", provider=provider, slug="test")
response = self.client.get(
@ -38,9 +37,7 @@ class TestSAMLProviderAPI(APITestCase):
self.client.logout()
provider = SAMLProvider.objects.create(
name="test",
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
)
Application.objects.create(name="test", provider=provider, slug="test")
response = self.client.get(
@ -56,9 +53,7 @@ class TestSAMLProviderAPI(APITestCase):
# Provider without application
provider = SAMLProvider.objects.create(
name="test",
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
)
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk}),
@ -79,11 +74,7 @@ class TestSAMLProviderAPI(APITestCase):
{
"file": metadata,
"name": "test",
"authorization_flow": Flow.objects.filter(
designation=FlowDesignation.AUTHORIZATION
)
.first()
.slug,
"authorization_flow": create_test_flow(FlowDesignation.AUTHORIZATION).slug,
},
format="multipart",
)
@ -100,11 +91,7 @@ class TestSAMLProviderAPI(APITestCase):
{
"file": metadata,
"name": "test",
"authorization_flow": Flow.objects.filter(
designation=FlowDesignation.AUTHORIZATION
)
.first()
.slug,
"authorization_flow": create_test_flow().slug,
},
format="multipart",
)

View File

@ -4,10 +4,9 @@ from base64 import b64encode
from django.http.request import QueryDict
from django.test import RequestFactory, TestCase
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow
from authentik.lib.tests.utils import get_request
from authentik.managed.manager import ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
@ -76,11 +75,9 @@ class TestAuthNRequest(TestCase):
def setUp(self):
ObjectManager().run()
cert = CertificateKeyPair.objects.first()
cert = create_test_cert()
self.provider: SAMLProvider = SAMLProvider.objects.create(
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
acs_url="http://testserver/source/saml/provider/acs/",
signing_kp=cert,
verification_kp=cert,
@ -90,7 +87,7 @@ class TestAuthNRequest(TestCase):
self.source = SAMLSource.objects.create(
slug="provider",
issuer="authentik",
pre_authentication_flow=Flow.objects.get(slug="default-source-pre-authentication"),
pre_authentication_flow=create_test_flow(),
signing_kp=cert,
)
self.factory = RequestFactory()
@ -186,9 +183,7 @@ class TestAuthNRequest(TestCase):
)
provider = SAMLProvider(
name="samltool",
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
acs_url="https://10.120.20.200/saml-sp/SAML2/POST",
audience="https://10.120.20.200/saml-sp/SAML2/POST",
issuer="https://10.120.20.200/saml-sp/SAML2/POST",
@ -206,16 +201,14 @@ class TestAuthNRequest(TestCase):
"""Test post request with static request"""
provider = SAMLProvider(
name="aws",
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
acs_url=(
"https://eu-central-1.signin.aws.amazon.com/platform/"
"saml/acs/2d737f96-55fb-4035-953e-5e24134eb778"
),
audience="https://10.120.20.200/saml-sp/SAML2/POST",
issuer="https://10.120.20.200/saml-sp/SAML2/POST",
signing_kp=CertificateKeyPair.objects.first(),
signing_kp=create_test_cert(),
)
parsed_request = AuthNRequestParser(provider).parse(POST_REQUEST)
self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re")
@ -223,7 +216,8 @@ class TestAuthNRequest(TestCase):
def test_request_attributes(self):
"""Test full SAML Request/Response flow, fully signed"""
http_request = get_request("/", user=User.objects.get(username="akadmin"))
user = create_test_admin_user()
http_request = get_request("/", user=user)
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
@ -235,11 +229,12 @@ class TestAuthNRequest(TestCase):
)
# Now create a response and convert it to string (provider)
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
self.assertIn("akadmin", response_proc.build_response())
self.assertIn(user.username, response_proc.build_response())
def test_request_attributes_invalid(self):
"""Test full SAML Request/Response flow, fully signed"""
http_request = get_request("/", user=User.objects.get(username="akadmin"))
user = create_test_admin_user()
http_request = get_request("/", user=user)
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
@ -255,7 +250,7 @@ class TestAuthNRequest(TestCase):
)
# Now create a response and convert it to string (provider)
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
self.assertIn("akadmin", response_proc.build_response())
self.assertIn(user.username, response_proc.build_response())
events = Event.objects.filter(
action=EventAction.CONFIGURATION_ERROR,

View File

@ -3,7 +3,7 @@
from django.test import TestCase
from authentik.flows.models import Flow
from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
@ -65,10 +65,10 @@ class TestServiceProviderMetadataParser(TestCase):
"""Test ServiceProviderMetadataParser parsing and creation of SAML Provider"""
def setUp(self) -> None:
self.flow = Flow.objects.first()
self.flow = create_test_flow()
def test_simple(self):
"""Test simple metadata without Singing"""
"""Test simple metadata without Signing"""
metadata = ServiceProviderMetadataParser().parse(METADATA_SIMPLE)
provider = metadata.to_provider("test", self.flow)
self.assertEqual(provider.acs_url, "http://localhost:8080/saml/acs")
@ -81,6 +81,7 @@ class TestServiceProviderMetadataParser(TestCase):
def test_with_signing_cert(self):
"""Test Metadata with signing cert"""
create_test_cert()
metadata = ServiceProviderMetadataParser().parse(METADATA_CERT)
provider = metadata.to_provider("test", self.flow)
self.assertEqual(provider.acs_url, "http://localhost:8080/apps/user_saml/saml/acs")

View File

@ -4,8 +4,7 @@ from base64 import b64encode
from django.test import RequestFactory, TestCase
from lxml import etree # nosec
from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.lib.tests.utils import get_request
from authentik.managed.manager import ObjectManager
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
@ -20,11 +19,9 @@ class TestSchema(TestCase):
def setUp(self):
ObjectManager().run()
cert = CertificateKeyPair.objects.first()
cert = create_test_cert()
self.provider: SAMLProvider = SAMLProvider.objects.create(
authorization_flow=Flow.objects.get(
slug="default-provider-authorization-implicit-consent"
),
authorization_flow=create_test_flow(),
acs_url="http://testserver/source/saml/provider/acs/",
signing_kp=cert,
verification_kp=cert,
@ -35,7 +32,7 @@ class TestSchema(TestCase):
slug="provider",
issuer="authentik",
signing_kp=cert,
pre_authentication_flow=Flow.objects.get(slug="default-source-pre-authentication"),
pre_authentication_flow=create_test_flow(),
)
self.factory = RequestFactory()

View File

@ -12,7 +12,7 @@ class TestRecovery(TestCase):
"""recovery tests"""
def setUp(self):
self.user = User.objects.create_user(username="recovery-test-user")
self.user: User = User.objects.create_user(username="recovery-test-user")
def test_create_key(self):
"""Test creation of a new key"""
@ -23,6 +23,13 @@ class TestRecovery(TestCase):
self.assertIn(token.key, out.getvalue())
self.assertEqual(len(Token.objects.all()), 1)
def test_create_key_invalid(self):
"""Test creation of a new key (invalid)"""
out = StringIO()
self.assertEqual(len(Token.objects.all()), 0)
call_command("create_recovery_key", "1", "foo", stderr=out)
self.assertIn("not found", out.getvalue())
def test_recovery_view(self):
"""Test recovery view"""
out = StringIO()
@ -35,3 +42,16 @@ class TestRecovery(TestCase):
"""Test recovery view with invalid token"""
response = self.client.get(reverse("authentik_recovery:use-token", kwargs={"key": "abc"}))
self.assertEqual(response.status_code, 404)
def test_recovery_admin_group_invalid(self):
"""Test creation of admin group"""
out = StringIO()
call_command("create_admin_group", "1", stderr=out)
self.assertIn("not found", out.getvalue())
def test_recovery_admin_group(self):
"""Test creation of admin group"""
out = StringIO()
call_command("create_admin_group", self.user.username, stdout=out)
self.assertIn("successfully added to", out.getvalue())
self.assertTrue(self.user.is_superuser)

70
authentik/root/asgi.py Normal file
View File

@ -0,0 +1,70 @@
"""
ASGI config for authentik project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
"""
import django
from channels.routing import ProtocolTypeRouter, URLRouter
from defusedxml import defuse_stdlib
from django.core.asgi import get_asgi_application
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib()
django.setup()
# pylint: disable=wrong-import-position
from authentik.root import websocket # noqa # isort:skip
class LifespanApp:
"""
temporary shim for https://github.com/django/channels/issues/1216
needed so that hypercorn doesn't display an error.
this uses ASGI 2.0 format, not the newer 3.0 single callable
"""
def __init__(self, scope):
self.scope = scope
async def __call__(self, receive, send):
if self.scope["type"] == "lifespan":
while True:
message = await receive()
if message["type"] == "lifespan.startup":
await send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
await send({"type": "lifespan.shutdown.complete"})
return
class RouteNotFoundMiddleware:
"""Middleware to ignore 404s for websocket requests
taken from https://github.com/django/daphne/issues/165#issuecomment-808284950"""
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
try:
return await self.app(scope, receive, send)
except ValueError as exc:
if "No route found for path" in str(exc) and scope["type"] == "websocket":
await send({"type": "websocket.close"})
else:
raise exc
application = SentryAsgiMiddleware(
ProtocolTypeRouter(
{
"http": get_asgi_application(),
"websocket": RouteNotFoundMiddleware(URLRouter(websocket.websocket_urlpatterns)),
"lifespan": LifespanApp,
}
)
)

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