Compare commits

..

132 Commits

Author SHA1 Message Date
eaad564e23 release: 2022.1.5 2022-02-09 22:31:26 +01:00
511a94975b website/docs: add 2022.1.5 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:31:14 +01:00
015810a2fd internal: fix CSRF error caused by Host header
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:53 +01:00
e70e6b84c2 internal: trace headers and url for backend requests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:50 +01:00
d0b9c9a26f internal: remove uvicorn server header
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:46 +01:00
3e403fa348 internal: improve error handling for internal reverse proxy
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:41 +01:00
48f4a971ef internal: don't attempt to lookup SNI Certificate if no SNI is sent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:39 +01:00
6314be14ad core: allow formatting strings to be used for applications' launch URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:29 +01:00
1a072c6c39 web/admin: fix mismatched icons in overview and lists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:26 +01:00
ef2eed0bdf outposts: fix compare_ports to support both service and container ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:20 +01:00
91227b1e96 outposts: fix service reconciler re-creating services
closes #2095

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:16 +01:00
67d68629da providers/proxy: fix Host/:Authority not being modified
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:08 +01:00
e875db8f66 stages/authenticator_validate: handle non-existent device_challenges
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:02 +01:00
055a76393d outposts: remove node_port on V1ServicePort checks to prevent service creation loops
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2095
2022-02-09 22:21:58 +01:00
0754821628 providers/proxy: improve error handling for invalid backend_override
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:55 +01:00
fca88d9896 sources/ldap: log entire exception
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:48 +01:00
dfe0404c51 sources/saml: fix server error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:24 +01:00
fa61696b46 sources/saml: fix incorrect ProtocolBinding being sent
closes #2213

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:15 +01:00
e5773738f4 outposts: fix channel not always having a logger attribute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:12 +01:00
cac8539d79 providers/proxy: fix nil error in claims
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:08 +01:00
cf600f6f26 build(deps): bump uvicorn from 0.17.1 to 0.17.3 (#2229)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.17.1 to 0.17.3.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.17.1...0.17.3)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-09 17:56:53 +01:00
49dfb4756e release: 2022.1.4 2022-02-01 20:12:55 +01:00
814758e2aa website/docs: prepare 2022.1.4
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-01 19:27:25 +01:00
5c42dac5e2 web/user: include locale code in locale selection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-01 19:19:37 +01:00
88603fa4f7 providers/proxy: set traefik labels using object_naming_template instead of UUID 2022-02-01 17:13:27 +00:00
0232c4e162 lifecycle: send analytics in gunicorn config to decrease outgoing requests when workers get restarted
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-01 15:01:43 +01:00
11753c1fe1 build(deps): bump django from 4.0.1 to 4.0.2 (#2204)
Bumps [django](https://github.com/django/django) from 4.0.1 to 4.0.2.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.0.1...4.0.2)

---
updated-dependencies:
- dependency-name: django
  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>
2022-02-01 10:59:04 +01:00
f5cc6c67ec providers/proxy: fix routing for external_host when using forward_auth_domain
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2180
2022-02-01 10:14:46 +01:00
8b8ed3527a build(deps): bump @typescript-eslint/parser in /web (#2200) 2022-02-01 09:11:41 +01:00
1aa0274e7c build(deps): bump @typescript-eslint/eslint-plugin in /web (#2201) 2022-02-01 09:09:34 +01:00
ecd33ca0c1 build(deps): bump github.com/go-openapi/runtime from 0.21.1 to 0.22.0 (#2202) 2022-02-01 09:09:18 +01:00
e93be0de9a sources/ldap: add list_flatten function to property mappings, enable on managed LDAP mappings
closes #2199

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-31 23:07:32 +01:00
a5adc4f8ed core: fix view_token permission not being assigned on token creation for non-admin user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-31 20:00:30 +01:00
a6baed9753 web/flows: fix width on flow container 2022-01-31 14:11:25 +00:00
ceaf832e63 root: remove boto integration in sentry to ease backup removal 2022-01-31 13:47:18 +00:00
a6b0b14685 Translate /web/src/locales/en.po in pl (#2197)
translation completed for the source file '/web/src/locales/en.po'
on the 'pl' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-01-31 14:17:26 +01:00
f679250edd lifecycle: remove gunicorn reload option
should help with #2159
2022-01-31 12:06:08 +00:00
acc4de2235 web: add pl locale 2022-01-31 11:50:05 +00:00
56a8276dbf website/integrations: update active directory docs (#2177) 2022-01-31 12:11:01 +01:00
6dfe6edbef website/integrations: add zulip (#2106)
* add zulip to sidebar links

* added Zulip chat integration documentation

* fix markdown typo

* add note about using Post for saml binding

* added missing ACS info and cleaned up

format matches other integration documents
2022-01-31 12:10:30 +01:00
6af4bd0d9a build(deps): bump construct-style-sheets-polyfill in /web (#2189)
Bumps [construct-style-sheets-polyfill](https://github.com/calebdwilliams/construct-style-sheets) from 3.0.5 to 3.1.0.
- [Release notes](https://github.com/calebdwilliams/construct-style-sheets/releases)
- [Changelog](https://github.com/calebdwilliams/construct-style-sheets/blob/main/CHANGELOG.md)
- [Commits](https://github.com/calebdwilliams/construct-style-sheets/commits)

---
updated-dependencies:
- dependency-name: construct-style-sheets-polyfill
  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>
2022-01-31 12:09:13 +01:00
7ee7f6bd6a Translate /web/src/locales/en.po in pl (#2196)
translation completed for the source file '/web/src/locales/en.po'
on the 'pl' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-01-31 10:00:37 +01:00
f8b8334010 build(deps): bump @patternfly/patternfly from 4.164.2 to 4.171.1 in /web (#2192) 2022-01-31 09:05:17 +01:00
d4b65dc4b4 build(deps): bump @sentry/browser from 6.17.2 to 6.17.3 in /web (#2191) 2022-01-31 09:04:40 +01:00
e4bbd3b1c0 build(deps): bump eslint from 8.7.0 to 8.8.0 in /web (#2190) 2022-01-31 09:03:47 +01:00
87de5e625d build(deps): bump @sentry/tracing from 6.17.2 to 6.17.3 in /web (#2193) 2022-01-31 09:03:32 +01:00
efbe51673e build(deps): bump pycryptodome from 3.13.0 to 3.14.0 (#2194) 2022-01-31 09:03:10 +01:00
a95bea53ea build(deps): bump github.com/prometheus/client_golang (#2195) 2022-01-31 09:02:56 +01:00
6021fc0f52 providers/proxy: fix backend override persisting for other users
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-30 22:29:34 +01:00
1415b68ff4 web: add es locale
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-30 21:43:55 +01:00
be6853ac52 Translate /web/src/locales/en.po in es (#2188)
translation completed for the source file '/web/src/locales/en.po'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-01-30 21:38:30 +01:00
7fd6be5abb providers/proxy: add backend_override
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-30 21:35:08 +01:00
91d6f572a5 scripts: cleanup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-30 21:34:37 +01:00
016a9ce34e build(deps): bump boto3 from 1.20.45 to 1.20.46 (#2187) 2022-01-30 00:52:25 +01:00
8adb95af7f build(deps): bump uvicorn from 0.17.0.post1 to 0.17.1 (#2186) 2022-01-30 00:52:08 +01:00
1dc54775d8 build(deps): bump requests-oauthlib from 1.3.0 to 1.3.1 (#2185) 2022-01-30 00:51:59 +01:00
370ef716b5 build(deps-dev): bump black from 21.12b0 to 22.1.0 (#2184) 2022-01-30 00:51:49 +01:00
16e56ad9ca website/docs: add rough documentation style guide
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-29 23:52:03 +01:00
b5b5a9eed3 web/admin: only check first half of locale when detecting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2178
2022-01-28 12:35:37 +01:00
8b22e7bcc3 core: compile backend translations (#2179) 2022-01-28 11:09:29 +01:00
d48b5b9511 Translate /locale/en/LC_MESSAGES/django.po in es (#2175)
translation completed for the source file '/locale/en/LC_MESSAGES/django.po'
on the 'es' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-01-28 09:55:56 +01:00
0eccaa3f1e build(deps): bump boto3 from 1.20.44 to 1.20.45 (#2176)
Bumps [boto3](https://github.com/boto/boto3) from 1.20.44 to 1.20.45.
- [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.44...1.20.45)

---
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>
2022-01-28 09:55:16 +01:00
67d550a80d providers/proxy: don't include hostname and scheme in redirect when we only got a path and not a full URL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-27 18:23:08 +01:00
ebb5711c32 providers/proxy: add support for X-Original-URI in nginx, better handle missing headers and report errors to authentik
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-27 18:14:02 +01:00
79ec872232 build(deps): bump @docusaurus/plugin-client-redirects in /website (#2173)
Bumps [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) from 2.0.0-beta.14 to 2.0.0-beta.15.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-beta.15/packages/docusaurus-plugin-client-redirects)

---
updated-dependencies:
- dependency-name: "@docusaurus/plugin-client-redirects"
  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>
2022-01-27 10:14:14 +01:00
4284e14ff7 build(deps): bump @docusaurus/preset-classic in /website (#2172)
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 2.0.0-beta.14 to 2.0.0-beta.15.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-beta.15/packages/docusaurus-preset-classic)

---
updated-dependencies:
- dependency-name: "@docusaurus/preset-classic"
  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>
2022-01-27 10:12:06 +01:00
92a09779d0 build(deps): bump boto3 from 1.20.43 to 1.20.44 (#2174) 2022-01-27 09:28:02 +01:00
14c621631d web: Update Web API Client version (#2170)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-01-26 23:30:56 +01:00
c55f503b9b release: 2022.1.3 2022-01-26 22:15:28 +01:00
a908cad976 website/docs: add release notes for 2022.1.3
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-26 21:41:15 +01:00
c2586557d8 root: fix redis passwords not being encoded correctly
closes #2130

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-26 20:45:45 +01:00
01c80a82e2 web/admin: fix SMS Stage form not working
closes #2127

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-26 20:39:38 +01:00
0d47654651 root: add max-requests for gunicorn and max tasks for celery
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-26 10:04:58 +01:00
1183095833 build(deps): bump @sentry/tracing from 6.17.1 to 6.17.2 in /web (#2162) 2022-01-26 09:35:10 +01:00
c281b11bdc build(deps): bump lit from 2.1.1 to 2.1.2 in /web (#2161) 2022-01-26 09:22:05 +01:00
61fe45a58c build(deps): bump @sentry/browser from 6.17.1 to 6.17.2 in /web (#2163) 2022-01-26 09:21:52 +01:00
d43aab479c build(deps): bump rollup from 2.66.0 to 2.66.1 in /web (#2164) 2022-01-26 09:21:43 +01:00
7f8383427a build(deps): bump sentry-sdk from 1.5.3 to 1.5.4 (#2165) 2022-01-26 09:21:24 +01:00
a06d6cf33d build(deps-dev): bump bandit from 1.7.1 to 1.7.2 (#2166) 2022-01-26 09:21:09 +01:00
5b7cb205c9 build(deps): bump boto3 from 1.20.42 to 1.20.43 (#2167) 2022-01-26 09:20:50 +01:00
293a932d20 build(deps-dev): bump coverage from 6.2 to 6.3 (#2168) 2022-01-26 09:20:34 +01:00
fff901ff03 rootL Fix goauthentik.io URL in Readme (#2158) 2022-01-25 20:36:44 +01:00
f47c936295 internal: add optional debug server listening on 9900
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-25 17:18:53 +01:00
88d5aec618 web/admin: fix links which look like labels
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-25 16:13:30 +01:00
96ae68cf09 internal: make error message less confusing
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-25 15:45:21 +01:00
63b3434b6f website/docs: improve nginx examples
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-25 14:25:21 +01:00
947ecec02b outposts/ldap: Fix more case sensitivity issues. (#2144) 2022-01-25 11:27:27 +01:00
1c2b452406 outposts/proxy: fix potential empty redirect, add tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2141
2022-01-25 10:57:53 +01:00
47777529ac build(deps): bump @formatjs/intl-listformat from 6.5.0 to 6.5.1 in /web (#2154) 2022-01-25 09:50:29 +01:00
949095c376 build(deps): bump @lingui/macro from 3.13.1 to 3.13.2 in /web (#2152) 2022-01-25 09:49:59 +01:00
4b112c2799 build(deps): bump @sentry/browser from 6.16.1 to 6.17.1 in /web (#2146) 2022-01-25 09:49:48 +01:00
291a2516b1 build(deps): bump @typescript-eslint/eslint-plugin in /web (#2149) 2022-01-25 09:49:29 +01:00
4dcfd021e2 build(deps): bump @lingui/detect-locale from 3.13.1 to 3.13.2 in /web (#2147) 2022-01-25 09:49:13 +01:00
ca50848db3 build(deps): bump boto3 from 1.20.41 to 1.20.42 (#2156) 2022-01-25 09:49:01 +01:00
0bb3e3c558 build(deps): bump @lingui/cli from 3.13.1 to 3.13.2 in /web (#2148) 2022-01-25 09:48:50 +01:00
e4b25809ab build(deps): bump @typescript-eslint/parser in /web (#2150) 2022-01-25 09:48:03 +01:00
7bf932f8e2 build(deps): bump @sentry/tracing from 6.16.1 to 6.17.1 in /web (#2151) 2022-01-25 09:47:52 +01:00
99d04528b0 build(deps): bump country-flag-icons from 1.4.19 to 1.4.20 in /web (#2153) 2022-01-25 09:47:43 +01:00
e48d172036 build(deps): bump @lingui/core from 3.13.1 to 3.13.2 in /web (#2155) 2022-01-25 09:47:34 +01:00
c2388137a8 build(deps): bump uvicorn from 0.17.0 to 0.17.0.post1 (#2157) 2022-01-25 09:47:05 +01:00
650e2cbc38 internal: remove duplicate log messages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 22:25:35 +01:00
b32800ea71 outposts/proxy: trace full headers to debug
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 22:08:31 +01:00
e1c0c0b20c internal: don't override server header
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 22:05:11 +01:00
fe39e39dcd lifecycle: make secret_key warning more prominent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2131
2022-01-24 21:52:16 +01:00
883f213b03 lifecycle: wait for db in worker
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 21:51:57 +01:00
538996f617 web: Update Web API Client version (#2143)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-01-24 21:46:39 +01:00
2f4c92deb9 Merge branch 'version-2022.1' 2022-01-24 21:42:12 +01:00
ef335ec083 outposts/proxy: add more test cases for domain-level auth
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 21:41:15 +01:00
07b09df3fe internal: add more outpost tests, add support for X-Original-URL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 20:50:13 +01:00
e70e031a1f internal: start adding tests to outpost
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 20:12:25 +01:00
c7ba183dc0 providers/proxy: fix traefik label
closes #2128

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 17:45:09 +01:00
3ed23a37ea website/docs: add 2022.1.2 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 11:34:13 +01:00
3d724db0e3 release: 2022.1.2 2022-01-24 11:28:00 +01:00
2997542114 lib: disable backup by default, add note to configuration 2022-01-24 10:00:15 +00:00
84b18fff96 ci: cache-v2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 09:37:04 +01:00
1dce408c72 internal/proxyv2: only allow access to /akprox in nginx mode when forward url could be extracted
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-24 09:30:33 +01:00
e5ff47bf14 build(deps): bump @lingui/cli from 3.13.0 to 3.13.1 in /web (#2133) 2022-01-24 08:49:03 +01:00
b53bf331c3 build(deps): bump @lingui/macro from 3.13.0 to 3.13.1 in /web (#2135) 2022-01-24 08:48:51 +01:00
90e9a8b34c build(deps): bump rollup from 2.64.0 to 2.66.0 in /web (#2139) 2022-01-24 08:48:26 +01:00
845f842783 build(deps): bump @lingui/core from 3.13.0 to 3.13.1 in /web (#2136) 2022-01-24 08:48:17 +01:00
7397849c60 build(deps): bump rapidoc from 9.1.3 to 9.1.4 in /website (#2132) 2022-01-24 08:47:45 +01:00
6dd46b5fc5 build(deps): bump @babel/core from 7.16.10 to 7.16.12 in /web (#2134) 2022-01-24 08:47:35 +01:00
89ca79ed10 build(deps): bump @lingui/detect-locale from 3.13.0 to 3.13.1 in /web (#2137) 2022-01-24 08:47:15 +01:00
713bef895c build(deps): bump rapidoc from 9.1.3 to 9.1.4 in /web (#2138) 2022-01-24 08:46:37 +01:00
925115e9ce build(deps): bump github.com/go-openapi/runtime from 0.21.0 to 0.21.1 (#2140) 2022-01-24 08:46:17 +01:00
42f5cf8c93 outposts: allow custom label for docker containers
closes #2128

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-23 21:55:58 +01:00
82cc1d536a providers/proxy: add PathPrefix to auto-traefik labels
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2128
2022-01-23 21:55:46 +01:00
08af2fd46b website/docs: deprecate inbuilt backup
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-23 21:51:22 +01:00
70e3b27a4d root: upgrade python dependencies
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-23 21:27:16 +01:00
6a411d7960 policies/hibp: ensure password is encodable
closes AUTHENTIK-1SA

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-23 21:23:24 +01:00
33567b56d7 lifecycle: replace lowercase, deprecated prometheus_multiproc_dir
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-01-23 21:21:06 +01:00
0c1954aeb7 web: Update Web API Client version (#2126)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-01-22 19:06:20 +01:00
106 changed files with 18127 additions and 2412 deletions

View File

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

View File

@ -40,7 +40,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
@ -56,7 +56,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
@ -79,7 +79,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: checkout stable
run: |
# Copy current, latest config to local
@ -121,7 +121,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
@ -148,7 +148,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
@ -185,7 +185,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
@ -230,7 +230,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}

View File

@ -30,9 +30,25 @@ jobs:
-w /app \
golangci/golangci-lint:v1.43 \
golangci-lint run -v --timeout 200s
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- name: Get dependencies
run: |
go get github.com/axw/gocov/gocov
go get github.com/AlekSi/gocov-xml
go get github.com/jstemmer/go-junit-report
- name: Go unittests
run: |
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./... | go-junit-report > junit.xml
ci-outpost-mark:
needs:
- lint-golint
- test-unittest
runs-on: ubuntu-latest
steps:
- run: echo mark

View File

@ -30,14 +30,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2022.1.1,
beryju/authentik:2022.1.5,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2022.1.1,
ghcr.io/goauthentik/server:2022.1.5,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2022.1.1', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2022.1.5', 'rc') }}
run: |
docker pull beryju/authentik:latest
docker tag beryju/authentik:latest beryju/authentik:stable
@ -78,14 +78,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-${{ matrix.type }}:2022.1.1,
beryju/authentik-${{ matrix.type }}:2022.1.5,
beryju/authentik-${{ matrix.type }}:latest,
ghcr.io/goauthentik/${{ matrix.type }}:2022.1.1,
ghcr.io/goauthentik/${{ matrix.type }}:2022.1.5,
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('2022.1.1', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2022.1.5', 'rc') }}
run: |
docker pull beryju/authentik-${{ matrix.type }}:latest
docker tag beryju/authentik-${{ matrix.type }}:latest beryju/authentik-${{ matrix.type }}:stable
@ -170,7 +170,7 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2022.1.1
version: authentik@2022.1.5
environment: beryjuorg-prod
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -26,7 +26,7 @@ jobs:
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v3-${{ hashFiles('**/poetry.lock') }}
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}

View File

@ -12,7 +12,8 @@
"totp",
"webauthn",
"traefik",
"passwordless"
"passwordless",
"kubernetes"
],
"python.linting.pylintEnabled": true,
"todo-tree.tree.showCountsInTree": true,

View File

@ -15,6 +15,9 @@ test-e2e-provider:
test-e2e-rest:
coverage run manage.py test tests/e2e/test_flows* tests/e2e/test_source*
test-go:
go test -timeout 0 -v -race -cover ./...
test:
coverage run manage.py test authentik
coverage html

View File

@ -57,4 +57,4 @@ DigitalOcean provides development and testing resources for authentik.
</a>
</p>
Netlify hosts the [goauthentik.io](goauthentik.io) site.
Netlify hosts the [goauthentik.io](https://goauthentik.io) site.

View File

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

View File

@ -1,13 +1,16 @@
"""Application API Views"""
from typing import Optional
from django.core.cache import cache
from django.db.models import QuerySet
from django.http.response import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django.utils.functional import SimpleLazyObject
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import ReadOnlyField
from rest_framework.fields import ReadOnlyField, SerializerMethodField
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
from rest_framework.response import Response
@ -39,11 +42,22 @@ def user_app_cache_key(user_pk: str) -> str:
class ApplicationSerializer(ModelSerializer):
"""Application Serializer"""
launch_url = ReadOnlyField(source="get_launch_url")
launch_url = SerializerMethodField()
provider_obj = ProviderSerializer(source="get_provider", required=False)
meta_icon = ReadOnlyField(source="get_meta_icon")
def get_launch_url(self, app: Application) -> Optional[str]:
"""Allow formatting of launch URL"""
url = app.get_launch_url()
if not url:
return url
user = self.context["request"].user
if isinstance(user, SimpleLazyObject):
user._setup()
user = user._wrapped
return url % user.__dict__
class Meta:
model = Application

View File

@ -3,7 +3,7 @@ from typing import Any
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import OpenApiResponse, extend_schema
from guardian.shortcuts import get_anonymous_user
from guardian.shortcuts import assign_perm, get_anonymous_user
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField
@ -95,10 +95,12 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
def perform_create(self, serializer: TokenSerializer):
if not self.request.user.is_superuser:
return serializer.save(
instance = serializer.save(
user=self.request.user,
expiring=self.request.user.attributes.get(USER_ATTRIBUTE_TOKEN_EXPIRING, True),
)
assign_perm("authentik_core.view_token_key", self.request.user, instance)
return instance
return super().perform_create(serializer)
@permission_required("authentik_core.view_token_key")

View File

@ -13,7 +13,9 @@ class TestApplicationsAPI(APITestCase):
def setUp(self) -> None:
self.user = create_test_admin_user()
self.allowed = Application.objects.create(name="allowed", slug="allowed")
self.allowed = Application.objects.create(
name="allowed", slug="allowed", meta_launch_url="https://goauthentik.io/%(username)s"
)
self.denied = Application.objects.create(name="denied", slug="denied")
PolicyBinding.objects.create(
target=self.denied,
@ -64,8 +66,8 @@ class TestApplicationsAPI(APITestCase):
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"launch_url": f"https://goauthentik.io/{self.user.username}",
"meta_launch_url": "https://goauthentik.io/%(username)s",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",
@ -100,8 +102,8 @@ class TestApplicationsAPI(APITestCase):
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"launch_url": f"https://goauthentik.io/{self.user.username}",
"meta_launch_url": "https://goauthentik.io/%(username)s",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",

View File

@ -30,6 +30,7 @@ class TestTokenAPI(APITestCase):
self.assertEqual(token.user, self.user)
self.assertEqual(token.intent, TokenIntents.INTENT_API)
self.assertEqual(token.expiring, True)
self.assertTrue(self.user.has_perm("authentik_core.view_token_key", token))
def test_token_create_invalid(self):
"""Test token creation endpoint (invalid data)"""

View File

@ -6,7 +6,7 @@ postgresql:
port: 5432
password: 'env://POSTGRES_PASSWORD'
backup:
enabled: true
enabled: false
s3_backup:
access_key: ""
secret_key: ""

View File

@ -32,6 +32,7 @@ class BaseEvaluator:
self._globals = {
"regex_match": BaseEvaluator.expr_regex_match,
"regex_replace": BaseEvaluator.expr_regex_replace,
"list_flatten": BaseEvaluator.expr_flatten,
"ak_is_group_member": BaseEvaluator.expr_is_group_member,
"ak_user_by": BaseEvaluator.expr_user_by,
"ak_logger": get_logger(),
@ -40,6 +41,15 @@ class BaseEvaluator:
self._context = {}
self._filename = "BaseEvalautor"
@staticmethod
def expr_flatten(value: list[Any] | Any) -> Optional[Any]:
"""Flatten `value` if its a list"""
if isinstance(value, list):
if len(value) < 1:
return None
return value[0]
return value
@staticmethod
def expr_regex_match(value: Any, regex: str) -> bool:
"""Expression Filter to run re.search"""

View File

@ -55,6 +55,10 @@ class OutpostConsumer(AuthJsonConsumer):
first_msg = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger = get_logger()
def connect(self):
super().connect()
uuid = self.scope["url_route"]["kwargs"]["pk"]
@ -65,7 +69,7 @@ class OutpostConsumer(AuthJsonConsumer):
)
if not outpost:
raise DenyConnection()
self.logger = get_logger().bind(outpost=outpost)
self.logger = self.logger.bind(outpost=outpost)
try:
self.accept()
except RuntimeError as exc:

View File

@ -106,9 +106,12 @@ class DockerController(BaseController):
).lower()
def _get_labels(self) -> dict[str, str]:
return {
labels = {
"io.goauthentik.outpost-uuid": self.outpost.pk.hex,
}
if self.outpost.config.docker_labels:
labels.update(self.outpost.config.docker_labels)
return labels
def _get_env(self) -> dict[str, str]:
return {

View File

@ -2,6 +2,7 @@
from pathlib import Path
from kubernetes.client.models.v1_container_port import V1ContainerPort
from kubernetes.client.models.v1_service_port import V1ServicePort
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate
@ -16,10 +17,31 @@ def get_namespace() -> str:
return "default"
def compare_ports(current: list[V1ContainerPort], reference: list[V1ContainerPort]):
def compare_port(
current: V1ServicePort | V1ContainerPort, reference: V1ServicePort | V1ContainerPort
) -> bool:
"""Compare a single port"""
if current.name != reference.name:
return False
if current.protocol != reference.protocol:
return False
if isinstance(current, V1ServicePort) and isinstance(reference, V1ServicePort):
# We only care about the target port
if current.target_port != reference.target_port:
return False
if isinstance(current, V1ContainerPort) and isinstance(reference, V1ContainerPort):
# We only care about the target port
if current.container_port != reference.container_port:
return False
return True
def compare_ports(
current: list[V1ServicePort | V1ContainerPort], reference: list[V1ServicePort | V1ContainerPort]
):
"""Compare ports of a list"""
if len(current) != len(reference):
raise NeedsRecreate()
for port in reference:
if port not in current:
if not any(compare_port(port, current_port) for current_port in current):
raise NeedsRecreate()

View File

@ -60,6 +60,7 @@ class OutpostConfig:
docker_network: Optional[str] = field(default=None)
docker_map_ports: bool = field(default=True)
docker_labels: Optional[dict[str, str]] = field(default=None)
container_image: Optional[str] = field(default=None)

View File

@ -45,7 +45,7 @@ class HaveIBeenPwendPolicy(Policy):
fields=request.context.keys(),
)
return PolicyResult(False, _("Password not set in context"))
password = request.context[self.password_field]
password = str(request.context[self.password_field])
pw_hash = sha1(password.encode("utf-8")).hexdigest() # nosec
url = f"https://api.pwnedpasswords.com/range/{pw_hash[:5]}"

View File

@ -23,10 +23,12 @@ class ProxyDockerController(DockerController):
proxy_provider: ProxyProvider
external_host_name = urlparse(proxy_provider.external_host)
hosts.append(f"`{external_host_name.netloc}`")
traefik_name = f"ak-outpost-{self.outpost.pk.hex}"
traefik_name = self.name
labels = super()._get_labels()
labels["traefik.enable"] = "true"
labels[f"traefik.http.routers.{traefik_name}-router.rule"] = f"Host({','.join(hosts)})"
labels[
f"traefik.http.routers.{traefik_name}-router.rule"
] = f"Host({','.join(hosts)}) && PathPrefix(`/akprox`)"
labels[f"traefik.http.routers.{traefik_name}-router.tls"] = "true"
labels[f"traefik.http.routers.{traefik_name}-router.service"] = f"{traefik_name}-service"
labels[

View File

@ -15,6 +15,7 @@ from authentik.providers.saml.processors.request_parser import AuthNRequestParse
from authentik.sources.saml.exceptions import MismatchedRequestID
from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.processors.constants import (
SAML_BINDING_REDIRECT,
SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
)
@ -98,6 +99,9 @@ class TestAuthNRequest(TestCase):
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
auth_n = request_proc.get_auth_n()
self.assertEqual(auth_n.attrib["ProtocolBinding"], SAML_BINDING_REDIRECT)
request = request_proc.build_auth_n()
# Now we check the ID and signature
parsed_request = AuthNRequestParser(self.provider).parse(

View File

@ -1,14 +1,4 @@
"""
Django settings for authentik project.
Generated by 'django-admin startproject' using Django 2.1.3.
For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""
"""root settings for authentik"""
import importlib
import logging
@ -18,24 +8,22 @@ from hashlib import sha512
from json import dumps
from tempfile import gettempdir
from time import time
from urllib.parse import quote
from urllib.parse import quote_plus
import structlog
from celery.schedules import crontab
from sentry_sdk import init as sentry_init
from sentry_sdk.api import set_tag
from sentry_sdk.integrations.boto3 import Boto3Integration
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.threading import ThreadingIntegration
from authentik import ENV_GIT_HASH_KEY, __version__, get_build_hash, get_full_version
from authentik import ENV_GIT_HASH_KEY, __version__, get_build_hash
from authentik.core.middleware import structlog_add_request_id
from authentik.lib.config import CONFIG
from authentik.lib.logging import add_process_id
from authentik.lib.sentry import before_send
from authentik.lib.utils.http import get_http_session
from authentik.lib.utils.reflection import get_env
from authentik.stages.password import BACKEND_APP_PASSWORD, BACKEND_INBUILT, BACKEND_LDAP
@ -220,7 +208,7 @@ if CONFIG.y_bool("redis.tls", False):
REDIS_CELERY_TLS_REQUIREMENTS = f"?ssl_cert_reqs={CONFIG.y('redis.tls_reqs')}"
_redis_url = (
f"{REDIS_PROTOCOL_PREFIX}:"
f"{quote(CONFIG.y('redis.password'))}@{quote(CONFIG.y('redis.host'))}:"
f"{quote_plus(CONFIG.y('redis.password'))}@{quote_plus(CONFIG.y('redis.host'))}:"
f"{int(CONFIG.y('redis.port'))}"
)
@ -347,6 +335,7 @@ LOCALE_PATHS = ["./locale"]
# Celery settings
# Add a 10 minute timeout to all Celery tasks.
CELERY_TASK_SOFT_TIME_LIMIT = 600
CELERY_WORKER_MAX_TASKS_PER_CHILD = 50
CELERY_BEAT_SCHEDULE = {
"clean_expired_models": {
"task": "authentik.core.tasks.clean_expired_models",
@ -407,7 +396,6 @@ if _ERROR_REPORTING:
DjangoIntegration(transaction_style="function_name"),
CeleryIntegration(),
RedisIntegration(),
Boto3Integration(),
ThreadingIntegration(propagate_hub=True),
],
before_send=before_send,
@ -424,29 +412,6 @@ if _ERROR_REPORTING:
"Error reporting is enabled",
env=CONFIG.y("error_reporting.environment", "customer"),
)
if not CONFIG.y_bool("disable_startup_analytics", False):
should_send = env not in ["dev", "ci"]
if should_send:
try:
get_http_session().post(
"https://goauthentik.io/api/event",
json={
"domain": "authentik",
"name": "pageview",
"referrer": get_full_version(),
"url": (
f"http://localhost/{env}?utm_source={get_full_version()}&utm_medium={env}"
),
},
headers={
"User-Agent": sha512(str(SECRET_KEY).encode("ascii")).hexdigest()[:16],
"Content-Type": "application/json",
},
timeout=5,
)
# pylint: disable=bare-except
except: # nosec
pass
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

View File

@ -35,21 +35,21 @@ class LDAPProviderManager(ObjectManager):
"goauthentik.io/sources/ldap/ms-userprincipalname",
name="authentik default Active Directory Mapping: userPrincipalName",
object_field="attributes.upn",
expression="return ldap.get('userPrincipalName')",
expression="return list_flatten(ldap.get('userPrincipalName'))",
),
EnsureExists(
LDAPPropertyMapping,
"goauthentik.io/sources/ldap/ms-givenName",
name="authentik default Active Directory Mapping: givenName",
object_field="attributes.givenName",
expression="return ldap.get('givenName')",
expression="return list_flatten(ldap.get('givenName'))",
),
EnsureExists(
LDAPPropertyMapping,
"goauthentik.io/sources/ldap/ms-sn",
name="authentik default Active Directory Mapping: sn",
object_field="attributes.sn",
expression="return ldap.get('sn')",
expression="return list_flatten(ldap.get('sn'))",
),
# OpenLDAP specific mappings
EnsureExists(

View File

@ -3,6 +3,7 @@ from ldap3.core.exceptions import LDAPException
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.reflection import class_to_path, path_to_class
from authentik.root.celery import CELERY_APP
from authentik.sources.ldap.models import LDAPSource
@ -52,5 +53,5 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str):
)
except LDAPException as exc:
# No explicit event is created here as .set_status with an error will do that
LOGGER.debug(exc)
LOGGER.warning(exception_to_string(exc))
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))

View File

@ -18,6 +18,8 @@ from authentik.sources.saml.processors.constants import (
RSA_SHA256,
RSA_SHA384,
RSA_SHA512,
SAML_BINDING_POST,
SAML_BINDING_REDIRECT,
SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_TRANSIENT,
@ -37,6 +39,15 @@ class SAMLBindingTypes(models.TextChoices):
POST = "POST", _("POST Binding")
POST_AUTO = "POST_AUTO", _("POST Binding with auto-confirmation")
@property
def uri(self) -> str:
"""Convert database field to URI"""
return {
SAMLBindingTypes.POST: SAML_BINDING_POST,
SAMLBindingTypes.POST_AUTO: SAML_BINDING_POST,
SAMLBindingTypes.REDIRECT: SAML_BINDING_REDIRECT,
}[self]
class SAMLNameIDPolicy(models.TextChoices):
"""SAML NameID Policies"""

View File

@ -10,7 +10,7 @@ from lxml.etree import Element # nosec
from authentik.providers.saml.utils import get_random_id
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode
from authentik.providers.saml.utils.time import get_time_string
from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.models import SAMLBindingTypes, SAMLSource
from authentik.sources.saml.processors.constants import (
DIGEST_ALGORITHM_TRANSLATION_MAP,
NS_MAP,
@ -62,7 +62,7 @@ class RequestProcessor:
auth_n_request.attrib["Destination"] = self.source.sso_url
auth_n_request.attrib["ID"] = self.request_id
auth_n_request.attrib["IssueInstant"] = self.issue_instant
auth_n_request.attrib["ProtocolBinding"] = self.source.binding_type
auth_n_request.attrib["ProtocolBinding"] = SAMLBindingTypes(self.source.binding_type).uri
auth_n_request.attrib["Version"] = "2.0"
# Create issuer object
auth_n_request.append(self.get_issuer())

View File

@ -196,7 +196,10 @@ class AuthenticatorValidateStageView(ChallengeStageView):
return super().get(request, *args, **kwargs)
def get_challenge(self) -> AuthenticatorValidationChallenge:
challenges = self.request.session["device_challenges"]
challenges = self.request.session.get("device_challenges")
if not challenges:
LOGGER.debug("Authenticator Validation stage ran without challenges")
return self.executor.stage_invalid()
return AuthenticatorValidationChallenge(
data={
"type": ChallengeTypes.NATIVE.value,

View File

@ -8,6 +8,7 @@ import (
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/common"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/ldap"
)
@ -27,6 +28,7 @@ func main() {
log.FieldKeyTime: "timestamp",
},
})
go debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
if !found {
fmt.Println("env AUTHENTIK_HOST not set!")

View File

@ -9,6 +9,7 @@ import (
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/common"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2"
)
@ -32,6 +33,7 @@ func main() {
log.FieldKeyTime: "timestamp",
},
})
go debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
if !found {
fmt.Println("env AUTHENTIK_HOST not set!")

View File

@ -11,6 +11,7 @@ import (
"goauthentik.io/internal/common"
"goauthentik.io/internal/config"
"goauthentik.io/internal/constants"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/gounicorn"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2"
@ -28,6 +29,7 @@ func main() {
log.FieldKeyTime: "timestamp",
},
})
go debug.EnableDebugServer()
l := log.WithField("logger", "authentik.root")
config.DefaultConfig()
err := config.LoadConfig("./authentik/lib/default.yml")

View File

@ -17,7 +17,7 @@ services:
image: redis:alpine
restart: unless-stopped
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.5}
restart: unless-stopped
command: server
environment:
@ -38,7 +38,7 @@ services:
- "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.5}
restart: unless-stopped
command: worker
environment:

6
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/garyburd/redigo v1.6.2 // indirect
github.com/getsentry/sentry-go v0.12.0
github.com/go-ldap/ldap/v3 v3.4.1
github.com/go-openapi/runtime v0.21.0
github.com/go-openapi/runtime v0.22.0
github.com/go-openapi/strfmt v0.21.1
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
@ -25,8 +25,10 @@ require (
github.com/pires/go-proxyproto v0.6.1
github.com/pkg/errors v0.9.1
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
github.com/prometheus/client_golang v1.12.0
github.com/prometheus/client_golang v1.12.1
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
goauthentik.io/api v0.2021125.1
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c

10
go.sum
View File

@ -183,8 +183,8 @@ github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29g
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
github.com/go-openapi/runtime v0.21.0 h1:giZ8eT26R+/rx6RX2MkYjZPY8vPYVKDhP/mOazrQHzM=
github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
github.com/go-openapi/runtime v0.22.0 h1:vY2D0u807kkcwidaj0YJuq4zyAWQnjLNDpJcVBrUFNs=
github.com/go-openapi/runtime v0.22.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
@ -471,8 +471,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg=
github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -489,6 +489,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=

View File

@ -25,4 +25,4 @@ func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s", FullVersion())
}
const VERSION = "2022.1.1"
const VERSION = "2022.1.5"

24
internal/debug/debug.go Normal file
View File

@ -0,0 +1,24 @@
package debug
import (
"net/http"
"net/http/pprof"
"os"
"strings"
log "github.com/sirupsen/logrus"
)
func EnableDebugServer() {
l := log.WithField("logger", "authentik.go_debugger")
if deb := os.Getenv("AUTHENTIK_DEBUG"); strings.ToLower(deb) != "true" {
l.Info("not enabling debug server, set `AUTHENTIK_DEBUG` to `true` to enable it.")
}
h := http.NewServeMux()
h.HandleFunc("/debug/pprof/", pprof.Index)
h.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
h.HandleFunc("/debug/pprof/profile", pprof.Profile)
h.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
h.HandleFunc("/debug/pprof/trace", pprof.Trace)
l.Println(http.ListenAndServe("0.0.0.0:9900", nil))
}

View File

@ -0,0 +1,66 @@
package ak
import (
"encoding/base64"
"fmt"
"math/rand"
"net/http"
"time"
"github.com/google/uuid"
"github.com/gorilla/securecookie"
log "github.com/sirupsen/logrus"
"goauthentik.io/api"
)
func TestSecret() string {
return base64.RawURLEncoding.EncodeToString(securecookie.GenerateRandomKey(32))
}
func MockConfig() api.Config {
return *api.NewConfig(
*api.NewErrorReportingConfig(false, "test", false, 0.0),
[]api.CapabilitiesEnum{},
100,
100,
100,
100,
)
}
func MockAK(outpost api.Outpost, globalConfig api.Config) *APIController {
config := api.NewConfiguration()
config.HTTPClient = &http.Client{
Transport: GetTLSTransport(),
}
token := TestSecret()
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))
// create the API client, with the transport
apiClient := api.NewAPIClient(config)
log := log.WithField("logger", "authentik.outpost.ak-api-controller")
log.WithField("name", outpost.Name).Debug("Fetched outpost configuration")
log.Debug("Fetched global configuration")
// doGlobalSetup is called by the OnRefresh handler, which ticks on start
// doGlobalSetup(outpost, akConfig)
ac := &APIController{
Client: apiClient,
GlobalConfig: globalConfig,
token: token,
logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
instanceUUID: uuid.New(),
Outpost: outpost,
wsBackoffMultiplier: 1,
refreshHandlers: make([]func(), 0),
}
ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset")
return ac
}

View File

@ -16,6 +16,7 @@ import (
"goauthentik.io/internal/outpost/ldap/flags"
"goauthentik.io/internal/outpost/ldap/metrics"
"goauthentik.io/internal/outpost/ldap/server"
"goauthentik.io/internal/outpost/ldap/utils"
)
const ContextUserKey = "ak_user"
@ -35,7 +36,7 @@ func NewDirectBinder(si server.LDAPServerInstance) *DirectBinder {
}
func (db *DirectBinder) GetUsername(dn string) (string, error) {
if !strings.HasSuffix(strings.ToLower(dn), strings.ToLower(db.si.GetBaseDN())) {
if !utils.HasSuffixNoCase(dn, db.si.GetBaseDN()) {
return "", errors.New("invalid base DN")
}
dns, err := goldap.ParseDN(dn)

View File

@ -140,26 +140,26 @@ func (pi *ProviderInstance) GetNeededObjects(scope int, baseDN string, filterOC
// If our requested base DN doesn't match any of the container DNs, then
// we're probably loading a user or group. If it does, then make sure our
// scope will eventually take us to users or groups.
if (baseDN == pi.BaseDN || strings.HasSuffix(baseDN, pi.UserDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetUserOCs()) {
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.UserDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetUserOCs()) {
if baseDN != pi.UserDN && baseDN != pi.BaseDN ||
baseDN == pi.BaseDN && scope > 1 ||
baseDN == pi.UserDN && scope > 0 {
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
strings.EqualFold(baseDN, pi.UserDN) && scope > 0 {
needUsers = true
}
}
if (baseDN == pi.BaseDN || strings.HasSuffix(baseDN, pi.GroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetGroupOCs()) {
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.GroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetGroupOCs()) {
if baseDN != pi.GroupDN && baseDN != pi.BaseDN ||
baseDN == pi.BaseDN && scope > 1 ||
baseDN == pi.GroupDN && scope > 0 {
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
strings.EqualFold(baseDN, pi.GroupDN) && scope > 0 {
needGroups = true
}
}
if (baseDN == pi.BaseDN || strings.HasSuffix(baseDN, pi.VirtualGroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetVirtualGroupOCs()) {
if (strings.EqualFold(baseDN, pi.BaseDN) || utils.HasSuffixNoCase(baseDN, pi.VirtualGroupDN)) && utils.IncludeObjectClass(filterOC, ldapConstants.GetVirtualGroupOCs()) {
if baseDN != pi.VirtualGroupDN && baseDN != pi.BaseDN ||
baseDN == pi.BaseDN && scope > 1 ||
baseDN == pi.VirtualGroupDN && scope > 0 {
strings.EqualFold(baseDN, pi.BaseDN) && scope > 1 ||
strings.EqualFold(baseDN, pi.VirtualGroupDN) && scope > 0 {
needUsers = true
}
}

View File

@ -36,7 +36,7 @@ func NewDirectSearcher(si server.LDAPServerInstance) *DirectSearcher {
func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult, error) {
accsp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.search.check_access")
baseDN := strings.ToLower(ds.si.GetBaseDN())
baseDN := ds.si.GetBaseDN()
filterOC, err := ldap.GetFilterObjectClass(req.Filter)
if err != nil {
@ -59,7 +59,7 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
}
if !strings.HasSuffix(req.BindDN, ","+baseDN) {
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": ds.si.GetOutpostName(),
"type": "search",
@ -105,7 +105,7 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope := req.SearchRequest.Scope
needUsers, needGroups := ds.si.GetNeededObjects(scope, req.BaseDN, filterOC)
if scope >= 0 && req.BaseDN == baseDN {
if scope >= 0 && strings.EqualFold(req.BaseDN, baseDN) {
if utils.IncludeObjectClass(filterOC, constants.GetDomainOCs()) {
entries = append(entries, ds.si.GetBaseEntry())
}
@ -209,8 +209,8 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, err
}
if scope >= 0 && (req.BaseDN == ds.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ds.si.GetBaseUserDN())) {
singleu := strings.HasSuffix(req.BaseDN, ","+ds.si.GetBaseUserDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ds.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ds.si.GetBaseUserDN())) {
singleu := utils.HasSuffixNoCase(req.BaseDN, ","+ds.si.GetBaseUserDN())
if !singleu && utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ds.si.GetBaseUserDN(), constants.OUUsers))
@ -220,7 +220,7 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && users != nil && utils.IncludeObjectClass(filterOC, constants.GetUserOCs()) {
for _, u := range *users {
entry := ds.si.UserEntry(u)
if req.BaseDN == entry.DN || !singleu {
if strings.EqualFold(req.BaseDN, entry.DN) || !singleu {
entries = append(entries, entry)
}
}
@ -229,8 +229,8 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope += 1 // Return the scope to what it was before we descended
}
if scope >= 0 && (req.BaseDN == ds.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ds.si.GetBaseGroupDN())) {
singleg := strings.HasSuffix(req.BaseDN, ","+ds.si.GetBaseGroupDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ds.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ds.si.GetBaseGroupDN())) {
singleg := utils.HasSuffixNoCase(req.BaseDN, ","+ds.si.GetBaseGroupDN())
if !singleg && utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ds.si.GetBaseGroupDN(), constants.OUGroups))
@ -240,7 +240,7 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && groups != nil && utils.IncludeObjectClass(filterOC, constants.GetGroupOCs()) {
for _, g := range *groups {
entry := group.FromAPIGroup(g, ds.si).Entry()
if req.BaseDN == entry.DN || !singleg {
if strings.EqualFold(req.BaseDN, entry.DN) || !singleg {
entries = append(entries, entry)
}
}
@ -249,8 +249,8 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope += 1 // Return the scope to what it was before we descended
}
if scope >= 0 && (req.BaseDN == ds.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ds.si.GetBaseVirtualGroupDN())) {
singlevg := strings.HasSuffix(req.BaseDN, ","+ds.si.GetBaseVirtualGroupDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ds.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ds.si.GetBaseVirtualGroupDN())) {
singlevg := utils.HasSuffixNoCase(req.BaseDN, ","+ds.si.GetBaseVirtualGroupDN())
if !singlevg || utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ds.si.GetBaseVirtualGroupDN(), constants.OUVirtualGroups))
@ -260,7 +260,7 @@ func (ds *DirectSearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && users != nil && utils.IncludeObjectClass(filterOC, constants.GetVirtualGroupOCs()) {
for _, u := range *users {
entry := group.FromAPIUser(u, ds.si).Entry()
if req.BaseDN == entry.DN || !singlevg {
if strings.EqualFold(req.BaseDN, entry.DN) || !singlevg {
entries = append(entries, entry)
}
}

View File

@ -39,7 +39,7 @@ func NewMemorySearcher(si server.LDAPServerInstance) *MemorySearcher {
func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult, error) {
accsp := sentry.StartSpan(req.Context(), "authentik.providers.ldap.search.check_access")
baseDN := strings.ToLower(ms.si.GetBaseDN())
baseDN := ms.si.GetBaseDN()
filterOC, err := ldap.GetFilterObjectClass(req.Filter)
if err != nil {
@ -62,7 +62,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, fmt.Errorf("Search Error: Anonymous BindDN not allowed %s", req.BindDN)
}
if !strings.HasSuffix(req.BindDN, ","+baseDN) {
if !utils.HasSuffixNoCase(req.BindDN, ","+baseDN) {
metrics.RequestsRejected.With(prometheus.Labels{
"outpost_name": ms.si.GetOutpostName(),
"type": "search",
@ -92,7 +92,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope := req.SearchRequest.Scope
needUsers, needGroups := ms.si.GetNeededObjects(scope, req.BaseDN, filterOC)
if scope >= 0 && req.BaseDN == baseDN {
if scope >= 0 && strings.EqualFold(req.BaseDN, baseDN) {
if utils.IncludeObjectClass(filterOC, constants.GetDomainOCs()) {
entries = append(entries, ms.si.GetBaseEntry())
}
@ -155,8 +155,8 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, err
}
if scope >= 0 && (req.BaseDN == ms.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ms.si.GetBaseUserDN())) {
singleu := strings.HasSuffix(req.BaseDN, ","+ms.si.GetBaseUserDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ms.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ms.si.GetBaseUserDN())) {
singleu := utils.HasSuffixNoCase(req.BaseDN, ","+ms.si.GetBaseUserDN())
if !singleu && utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ms.si.GetBaseUserDN(), constants.OUUsers))
@ -166,7 +166,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && users != nil && utils.IncludeObjectClass(filterOC, constants.GetUserOCs()) {
for _, u := range *users {
entry := ms.si.UserEntry(u)
if req.BaseDN == entry.DN || !singleu {
if strings.EqualFold(req.BaseDN, entry.DN) || !singleu {
entries = append(entries, entry)
}
}
@ -175,8 +175,8 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope += 1 // Return the scope to what it was before we descended
}
if scope >= 0 && (req.BaseDN == ms.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ms.si.GetBaseGroupDN())) {
singleg := strings.HasSuffix(req.BaseDN, ","+ms.si.GetBaseGroupDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ms.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ms.si.GetBaseGroupDN())) {
singleg := utils.HasSuffixNoCase(req.BaseDN, ","+ms.si.GetBaseGroupDN())
if !singleg && utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ms.si.GetBaseGroupDN(), constants.OUGroups))
@ -185,7 +185,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && groups != nil && utils.IncludeObjectClass(filterOC, constants.GetGroupOCs()) {
for _, g := range groups {
if req.BaseDN == g.DN || !singleg {
if strings.EqualFold(req.BaseDN, g.DN) || !singleg {
entries = append(entries, g.Entry())
}
}
@ -194,8 +194,8 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
scope += 1 // Return the scope to what it was before we descended
}
if scope >= 0 && (req.BaseDN == ms.si.GetBaseDN() || strings.HasSuffix(req.BaseDN, ms.si.GetBaseVirtualGroupDN())) {
singlevg := strings.HasSuffix(req.BaseDN, ","+ms.si.GetBaseVirtualGroupDN())
if scope >= 0 && (strings.EqualFold(req.BaseDN, ms.si.GetBaseDN()) || utils.HasSuffixNoCase(req.BaseDN, ms.si.GetBaseVirtualGroupDN())) {
singlevg := utils.HasSuffixNoCase(req.BaseDN, ","+ms.si.GetBaseVirtualGroupDN())
if !singlevg && utils.IncludeObjectClass(filterOC, constants.GetContainerOCs()) {
entries = append(entries, utils.GetContainerEntry(filterOC, ms.si.GetBaseVirtualGroupDN(), constants.OUVirtualGroups))
@ -205,7 +205,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
if scope >= 0 && users != nil && utils.IncludeObjectClass(filterOC, constants.GetVirtualGroupOCs()) {
for _, u := range *users {
entry := group.FromAPIUser(u, ms.si).Entry()
if req.BaseDN == entry.DN || !singlevg {
if strings.EqualFold(req.BaseDN, entry.DN) || !singlevg {
entries = append(entries, entry)
}
}

View File

@ -26,7 +26,6 @@ type Request struct {
func NewRequest(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (*Request, *sentry.Span) {
rid := uuid.New().String()
bindDN = strings.ToLower(bindDN)
searchReq.BaseDN = strings.ToLower(searchReq.BaseDN)
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search"))
span.Description = fmt.Sprintf("%s (%s)", searchReq.BaseDN, ldap.ScopeMap[searchReq.Scope])
span.SetTag("request_uid", rid)

View File

@ -2,6 +2,7 @@ package utils
import (
"reflect"
"strings"
"github.com/nmcclain/ldap"
log "github.com/sirupsen/logrus"
@ -117,3 +118,7 @@ func GetContainerEntry(filterOC string, dn string, ou string) *ldap.Entry {
return nil
}
func HasSuffixNoCase(s1 string, s2 string) bool {
return strings.HasSuffix(strings.ToLower(s1), strings.ToLower(s2))
}

View File

@ -1,6 +1,8 @@
package utils
import (
"strings"
goldap "github.com/go-ldap/ldap/v3"
ber "github.com/nmcclain/asn1-ber"
"github.com/nmcclain/ldap"
@ -41,7 +43,7 @@ func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet)
// Switch on type of the value, then check the key
switch vv := v.(type) {
case string:
switch k {
switch strings.ToLower(k.(string)) {
case "cn":
return req.Name(vv), false
case "member":
@ -54,7 +56,7 @@ func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet)
username := userDN.RDNs[0].Attributes[0].Value
// If the DN's first ou is virtual-groups, ignore this filter
if len(userDN.RDNs) > 1 {
if userDN.RDNs[1].Attributes[0].Value == constants.OUVirtualGroups || userDN.RDNs[1].Attributes[0].Value == constants.OUGroups {
if strings.EqualFold(userDN.RDNs[1].Attributes[0].Value, constants.OUVirtualGroups) || strings.EqualFold(userDN.RDNs[1].Attributes[0].Value, constants.OUGroups) {
// Since we know we're not filtering anything, skip this request
return req, true
}

View File

@ -46,6 +46,7 @@ type Application struct {
log *log.Entry
mux *mux.Router
ak *ak.APIController
errorTemplates *template.Template
}
@ -93,6 +94,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore
httpClient: c,
mux: mux,
errorTemplates: templates.GetTemplates(),
ak: ak,
}
a.sessions = a.getStore(p)
mux.Use(web.NewLoggingHandler(muxLogger, func(l *log.Entry, r *http.Request) *log.Entry {

View File

@ -1,18 +1,19 @@
package application
type ProxyClaims struct {
UserAttributes map[string]interface{} `json:"user_attributes"`
UserAttributes map[string]interface{} `json:"user_attributes"`
BackendOverride string `json:"backend_override"`
}
type Claims struct {
Sub string `json:"sub"`
Exp int `json:"exp"`
Email string `json:"email"`
Verified bool `json:"email_verified"`
Proxy ProxyClaims `json:"ak_proxy"`
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"groups"`
Sub string `json:"sub"`
Exp int `json:"exp"`
Email string `json:"email"`
Verified bool `json:"email_verified"`
Proxy *ProxyClaims `json:"ak_proxy"`
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"groups"`
RawToken string
}

View File

@ -1,7 +1,9 @@
package application
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http"
"net/url"
@ -57,7 +59,8 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) {
}
}
func (a *Application) getTraefikForwardUrl(r *http.Request) *url.URL {
// getTraefikForwardUrl See https://doc.traefik.io/traefik/middlewares/forwardauth/
func (a *Application) getTraefikForwardUrl(r *http.Request) (*url.URL, error) {
u, err := url.Parse(fmt.Sprintf(
"%s://%s%s",
r.Header.Get("X-Forwarded-Proto"),
@ -65,33 +68,63 @@ func (a *Application) getTraefikForwardUrl(r *http.Request) *url.URL {
r.Header.Get("X-Forwarded-Uri"),
))
if err != nil {
a.log.WithError(err).Warning("Failed to parse URL from traefik")
return r.URL
return nil, err
}
a.log.WithField("url", u.String()).Trace("traefik forwarded url")
return u
return u, nil
}
func (a *Application) IsAllowlisted(r *http.Request) bool {
url := r.URL
// In Forward auth mode, we can't directly match against the requested URL
// Since that would be /akprox/auth/...
if a.Mode() == api.PROXYMODE_FORWARD_SINGLE || a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
// For traefik, we can get the Upstream URL from headers
// For nginx we can attempt to as well, but it's not guaranteed to work.
if strings.HasPrefix(r.URL.Path, "/akprox/auth") {
url = a.getTraefikForwardUrl(r)
// getNginxForwardUrl See https://github.com/kubernetes/ingress-nginx/blob/main/rootfs/etc/nginx/template/nginx.tmpl
func (a *Application) getNginxForwardUrl(r *http.Request) (*url.URL, error) {
ou := r.Header.Get("X-Original-URI")
if ou != "" {
// Turn this full URL into a relative URL
u := &url.URL{
Host: "",
Scheme: "",
Path: ou,
}
a.log.WithField("url", u.String()).Info("building forward URL from X-Original-URI")
return u, nil
}
for _, u := range a.UnauthenticatedRegex {
h := r.Header.Get("X-Original-URL")
if len(h) < 1 {
return nil, errors.New("no forward URL found")
}
u, err := url.Parse(h)
if err != nil {
a.log.WithError(err).Warning("failed to parse URL from nginx")
return nil, err
}
a.log.WithField("url", u.String()).Trace("nginx forwarded url")
return u, nil
}
func (a *Application) ReportMisconfiguration(r *http.Request, msg string, fields map[string]interface{}) {
fields["message"] = msg
a.log.WithFields(fields).Error("Reporting configuration error")
req := api.EventRequest{
Action: api.EVENTACTIONS_CONFIGURATION_ERROR,
App: "authentik.providers.proxy", // must match python apps.py name
ClientIp: *api.NewNullableString(api.PtrString(r.RemoteAddr)),
Context: &fields,
}
_, _, err := a.ak.Client.EventsApi.EventsEventsCreate(context.Background()).EventRequest(req).Execute()
if err != nil {
a.log.WithError(err).Warning("failed to report configuration error")
}
}
func (a *Application) IsAllowlisted(u *url.URL) bool {
for _, ur := range a.UnauthenticatedRegex {
var testString string
if a.Mode() == api.PROXYMODE_PROXY || a.Mode() == api.PROXYMODE_FORWARD_SINGLE {
testString = url.Path
testString = u.Path
} else {
testString = url.String()
testString = u.String()
}
a.log.WithField("regex", u.String()).WithField("url", testString).Trace("Matching URL against allow list")
if u.MatchString(testString) {
if ur.MatchString(testString) {
return true
}
}

View File

@ -25,13 +25,27 @@ func (a *Application) configureForward() error {
}
func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Request) {
a.log.WithField("header", r.Header).Trace("tracing headers for debug")
// First check if we've got everything we need
fwd, err := a.getTraefikForwardUrl(r)
if err != nil {
a.ReportMisconfiguration(r, fmt.Sprintf("Outpost %s (Provider %s) failed to detect a forward URL from Traefik", a.outpostName, a.proxyConfig.Name), map[string]interface{}{
"provider": a.proxyConfig.Name,
"outpost": a.outpostName,
"url": r.URL.String(),
"headers": cleanseHeaders(r.Header),
})
http.Error(rw, "configuration error", http.StatusInternalServerError)
return
}
claims, err := a.getClaims(r)
if claims != nil && err == nil {
a.addHeaders(rw.Header(), claims)
rw.Header().Set("User-Agent", r.Header.Get("User-Agent"))
a.log.WithField("headers", rw.Header()).Trace("headers written to forward_auth")
return
} else if claims == nil && a.IsAllowlisted(r) {
} else if claims == nil && a.IsAllowlisted(fwd) {
a.log.Trace("path can be accessed without authentication")
return
}
@ -45,15 +59,18 @@ func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Reque
if *a.proxyConfig.Mode == api.PROXYMODE_FORWARD_SINGLE {
host = web.GetHost(r)
} else if *a.proxyConfig.Mode == api.PROXYMODE_FORWARD_DOMAIN {
eh, _ := url.Parse(a.proxyConfig.ExternalHost)
host = eh.Host
eh, err := url.Parse(a.proxyConfig.ExternalHost)
if err != nil {
a.log.WithField("host", a.proxyConfig.ExternalHost).WithError(err).Warning("invalid external_host")
} else {
host = eh.Host
}
}
// set the redirect flag to the current URL we have, since we redirect
// to a (possibly) different domain, but we want to be redirected back
// to the application
// see https://doc.traefik.io/traefik/middlewares/forwardauth/
// X-Forwarded-Uri is only the path, so we need to build the entire URL
s.Values[constants.SessionRedirect] = a.getTraefikForwardUrl(r).String()
s.Values[constants.SessionRedirect] = fwd.String()
err = s.Save(r, rw)
if err != nil {
a.log.WithError(err).Warning("failed to save session before redirect")
@ -69,6 +86,19 @@ func (a *Application) forwardHandleTraefik(rw http.ResponseWriter, r *http.Reque
}
func (a *Application) forwardHandleNginx(rw http.ResponseWriter, r *http.Request) {
a.log.WithField("header", r.Header).Trace("tracing headers for debug")
fwd, err := a.getNginxForwardUrl(r)
if err != nil {
a.ReportMisconfiguration(r, fmt.Sprintf("Outpost %s (Provider %s) failed to detect a forward URL from nginx", a.outpostName, a.proxyConfig.Name), map[string]interface{}{
"provider": a.proxyConfig.Name,
"outpost": a.outpostName,
"url": r.URL.String(),
"headers": cleanseHeaders(r.Header),
})
http.Error(rw, "configuration error", http.StatusInternalServerError)
return
}
claims, err := a.getClaims(r)
if claims != nil && err == nil {
a.addHeaders(rw.Header(), claims)
@ -76,13 +106,23 @@ func (a *Application) forwardHandleNginx(rw http.ResponseWriter, r *http.Request
rw.WriteHeader(200)
a.log.WithField("headers", rw.Header()).Trace("headers written to forward_auth")
return
} else if claims == nil && a.IsAllowlisted(r) {
} else if claims == nil && a.IsAllowlisted(fwd) {
a.log.Trace("path can be accessed without authentication")
return
}
if strings.HasPrefix(a.getTraefikForwardUrl(r).Path, "/akprox") {
a.log.WithField("url", r.URL.String()).Trace("path begins with /akprox, allowing access")
return
s, _ := a.sessions.Get(r, constants.SeesionName)
s.Values[constants.SessionRedirect] = fwd.String()
err = s.Save(r, rw)
if err != nil {
a.log.WithError(err).Warning("failed to save session before redirect")
}
if fwd.String() != r.URL.String() {
if strings.HasPrefix(fwd.Path, "/akprox") {
a.log.WithField("url", r.URL.String()).Trace("path begins with /akprox, allowing access")
return
}
}
http.Error(rw, "unauthorized request", http.StatusUnauthorized)
}

View File

@ -0,0 +1,134 @@
package application
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/proxyv2/constants"
)
func TestForwardHandleNginx_Single_Blank(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
}
func TestForwardHandleNginx_Single_Skip(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
req.Header.Set("X-Original-URL", "http://test.goauthentik.io/skip")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
}
func TestForwardHandleNginx_Single_Headers(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
req.Header.Set("X-Original-URL", "http://test.goauthentik.io/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, rr.Code, http.StatusUnauthorized)
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_URI(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "https://foo.bar/akprox/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, rr.Code, http.StatusUnauthorized)
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleNginx_Single_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
req.Header.Set("X-Original-URI", "/")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
UserAttributes: map[string]interface{}{
"username": "foo",
"password": "bar",
"additionalHeaders": map[string]interface{}{
"foo": "bar",
},
},
},
}
err := a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
rr = httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
h := rr.Result().Header
assert.Equal(t, []string{"Basic Zm9vOmJhcg=="}, h["Authorization"])
assert.Equal(t, []string{"bar"}, h["Foo"])
assert.Equal(t, []string{""}, h["User-Agent"])
assert.Equal(t, []string{""}, h["X-Authentik-Email"])
assert.Equal(t, []string{""}, h["X-Authentik-Groups"])
assert.Equal(t, []string{""}, h["X-Authentik-Jwt"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-App"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Jwks"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Outpost"])
assert.Equal(t, []string{""}, h["X-Authentik-Name"])
assert.Equal(t, []string{"foo"}, h["X-Authentik-Uid"])
assert.Equal(t, []string{""}, h["X-Authentik-Username"])
}
func TestForwardHandleNginx_Domain_Blank(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.CookieDomain = api.PtrString("foo")
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
}
func TestForwardHandleNginx_Domain_Header(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.CookieDomain = api.PtrString("foo")
a.proxyConfig.ExternalHost = "http://auth.test.goauthentik.io"
req, _ := http.NewRequest("GET", "/akprox/auth/nginx", nil)
req.Header.Set("X-Original-URL", "http://test.goauthentik.io/app")
rr := httptest.NewRecorder()
a.forwardHandleNginx(rr, req)
assert.Equal(t, http.StatusUnauthorized, rr.Code)
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}

View File

@ -0,0 +1,132 @@
package application
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/proxyv2/constants"
)
func TestForwardHandleTraefik_Single_Blank(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
}
func TestForwardHandleTraefik_Single_Skip(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
req.Header.Set("X-Forwarded-Proto", "http")
req.Header.Set("X-Forwarded-Host", "test.goauthentik.io")
req.Header.Set("X-Forwarded-Uri", "/skip")
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
}
func TestForwardHandleTraefik_Single_Headers(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
req.Header.Set("X-Forwarded-Proto", "http")
req.Header.Set("X-Forwarded-Host", "test.goauthentik.io")
req.Header.Set("X-Forwarded-Uri", "/app")
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
assert.Equal(t, rr.Code, http.StatusTemporaryRedirect)
loc, _ := rr.Result().Location()
assert.Equal(t, loc.String(), "http://test.goauthentik.io/akprox/start")
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}
func TestForwardHandleTraefik_Single_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
req.Header.Set("X-Forwarded-Proto", "http")
req.Header.Set("X-Forwarded-Host", "test.goauthentik.io")
req.Header.Set("X-Forwarded-Uri", "/app")
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
UserAttributes: map[string]interface{}{
"username": "foo",
"password": "bar",
"additionalHeaders": map[string]interface{}{
"foo": "bar",
},
},
},
}
err := a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
rr = httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
h := rr.Result().Header
assert.Equal(t, []string{"Basic Zm9vOmJhcg=="}, h["Authorization"])
assert.Equal(t, []string{"bar"}, h["Foo"])
assert.Equal(t, []string{""}, h["User-Agent"])
assert.Equal(t, []string{""}, h["X-Authentik-Email"])
assert.Equal(t, []string{""}, h["X-Authentik-Groups"])
assert.Equal(t, []string{""}, h["X-Authentik-Jwt"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-App"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Jwks"])
assert.Equal(t, []string{""}, h["X-Authentik-Meta-Outpost"])
assert.Equal(t, []string{""}, h["X-Authentik-Name"])
assert.Equal(t, []string{"foo"}, h["X-Authentik-Uid"])
assert.Equal(t, []string{""}, h["X-Authentik-Username"])
}
func TestForwardHandleTraefik_Domain_Blank(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.CookieDomain = api.PtrString("foo")
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
}
func TestForwardHandleTraefik_Domain_Header(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.CookieDomain = api.PtrString("foo")
a.proxyConfig.ExternalHost = "http://auth.test.goauthentik.io"
req, _ := http.NewRequest("GET", "/akprox/auth/traefik", nil)
req.Header.Set("X-Forwarded-Proto", "http")
req.Header.Set("X-Forwarded-Host", "test.goauthentik.io")
req.Header.Set("X-Forwarded-Uri", "/app")
rr := httptest.NewRecorder()
a.forwardHandleTraefik(rr, req)
assert.Equal(t, http.StatusTemporaryRedirect, rr.Code)
loc, _ := rr.Result().Location()
assert.Equal(t, "http://auth.test.goauthentik.io/akprox/start", loc.String())
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "http://test.goauthentik.io/app", s.Values[constants.SessionRedirect])
}

View File

@ -35,7 +35,7 @@ func (a *Application) configureProxy() error {
rp.ModifyResponse = a.proxyModifyResponse
a.mux.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
claims, err := a.getClaims(r)
if claims == nil && a.IsAllowlisted(r) {
if claims == nil && a.IsAllowlisted(r.URL) {
a.log.Trace("path can be accessed without authentication")
} else if claims == nil && err != nil {
a.redirectToStart(rw, r)
@ -60,7 +60,7 @@ func (a *Application) configureProxy() error {
}
metrics.UpstreamTiming.With(prometheus.Labels{
"outpost_name": a.outpostName,
"upstream_host": u.String(),
"upstream_host": r.URL.Host,
"scheme": r.URL.Scheme,
"method": r.Method,
"path": r.URL.Path,
@ -71,13 +71,27 @@ func (a *Application) configureProxy() error {
return nil
}
func (a *Application) proxyModifyRequest(u *url.URL) func(req *http.Request) {
return func(req *http.Request) {
req.URL.Scheme = u.Scheme
req.URL.Host = u.Host
func (a *Application) proxyModifyRequest(ou *url.URL) func(req *http.Request) {
return func(r *http.Request) {
claims, _ := a.getClaims(r)
r.URL.Scheme = ou.Scheme
r.URL.Host = ou.Host
r.Host = ou.Host
if claims != nil && claims.Proxy != nil && claims.Proxy.BackendOverride != "" {
u, err := url.Parse(claims.Proxy.BackendOverride)
if err != nil {
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
} else {
r.URL.Scheme = u.Scheme
r.URL.Host = u.Host
r.Host = u.Host
}
}
a.log.WithField("upstream_url", r.URL.String()).Trace("final upstream url")
}
}
func (a *Application) proxyModifyResponse(res *http.Response) error {
res.Header.Set("X-Powered-By", "authentik_proxy2")
return nil
}

View File

@ -0,0 +1,81 @@
package application
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/internal/outpost/proxyv2/constants"
)
func TestProxy_ModifyRequest(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "backend:8012", req.URL.Host)
assert.Equal(t, "backend:8012", req.Host)
}
func TestProxy_ModifyRequest_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
rr := httptest.NewRecorder()
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
BackendOverride: "http://other-backend:8123",
},
}
err = a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "other-backend:8123", req.URL.Host)
assert.Equal(t, "other-backend:8123", req.Host)
}
func TestProxy_ModifyRequest_Claims_Invalid(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
rr := httptest.NewRecorder()
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
BackendOverride: ":qewr",
},
}
err = a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "backend:8012", req.URL.Host)
assert.Equal(t, "backend:8012", req.Host)
}

View File

@ -0,0 +1,40 @@
package application
import (
"net/http"
"github.com/quasoft/memstore"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/ak"
)
func newTestApplication() *Application {
a, _ := NewApplication(
api.ProxyOutpostConfig{
Name: ak.TestSecret(),
ClientId: api.PtrString(ak.TestSecret()),
ClientSecret: api.PtrString(ak.TestSecret()),
CookieSecret: api.PtrString(ak.TestSecret()),
CookieDomain: api.PtrString(""),
Mode: api.PROXYMODE_FORWARD_SINGLE.Ptr(),
SkipPathRegex: api.PtrString("/skip.*"),
BasicAuthEnabled: api.PtrBool(true),
BasicAuthUserAttribute: api.PtrString("username"),
BasicAuthPasswordAttribute: api.PtrString("password"),
},
http.DefaultClient,
nil,
ak.MockAK(
api.Outpost{
Config: map[string]interface{}{
"authentik_host": ak.TestSecret(),
},
},
ak.MockConfig(),
),
)
a.sessions = memstore.NewMemStore(
[]byte(ak.TestSecret()),
)
return a
}

View File

@ -26,21 +26,14 @@ func (a *Application) redirectToStart(rw http.ResponseWriter, r *http.Request) {
if err == nil {
a.log.WithError(err).Warning("failed to decode session")
}
redirectUrl := r.URL.String()
// simple way to copy the URL
u, _ := url.Parse(redirectUrl)
// In proxy and forward_single mode we only have one URL that we route on
// if we somehow got here without that URL, make sure we're at least redirected back to it
if a.Mode() == api.PROXYMODE_PROXY || a.Mode() == api.PROXYMODE_FORWARD_SINGLE {
u.Host = a.proxyConfig.ExternalHost
}
redirectUrl := urlJoin(a.proxyConfig.ExternalHost, r.URL.Path)
if a.Mode() == api.PROXYMODE_FORWARD_DOMAIN {
dom := strings.TrimPrefix(*a.proxyConfig.CookieDomain, ".")
// In forward_domain we only check that the current URL's host
// ends with the cookie domain (remove the leading period if set)
if !strings.HasSuffix(r.URL.Hostname(), dom) {
a.log.WithField("url", r.URL.String()).WithField("cd", dom).Warning("Invalid redirect found")
redirectUrl = ""
redirectUrl = a.proxyConfig.ExternalHost
}
}
s.Values[constants.SessionRedirect] = redirectUrl
@ -94,3 +87,13 @@ func contains(s []string, e string) bool {
}
return false
}
func cleanseHeaders(headers http.Header) map[string]string {
h := make(map[string]string)
for hk, hv := range headers {
if len(hv) > 0 {
h[hk] = hv[0]
}
}
return h
}

View File

@ -0,0 +1,81 @@
package application
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/proxyv2/constants"
)
func TestRedirectToStart_Proxy(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_PROXY.Ptr()
a.proxyConfig.ExternalHost = "https://test.goauthentik.io"
req, _ := http.NewRequest("GET", "/foo/bar/baz", nil)
rr := httptest.NewRecorder()
a.redirectToStart(rr, req)
assert.Equal(t, http.StatusFound, rr.Code)
loc, _ := rr.Result().Location()
assert.Equal(t, "https://test.goauthentik.io/akprox/start", loc.String())
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "https://test.goauthentik.io/foo/bar/baz", s.Values[constants.SessionRedirect])
}
func TestRedirectToStart_Forward(t *testing.T) {
a := newTestApplication()
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_SINGLE.Ptr()
a.proxyConfig.ExternalHost = "https://test.goauthentik.io"
req, _ := http.NewRequest("GET", "/foo/bar/baz", nil)
rr := httptest.NewRecorder()
a.redirectToStart(rr, req)
assert.Equal(t, http.StatusFound, rr.Code)
loc, _ := rr.Result().Location()
assert.Equal(t, "https://test.goauthentik.io/akprox/start", loc.String())
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "https://test.goauthentik.io/foo/bar/baz", s.Values[constants.SessionRedirect])
}
func TestRedirectToStart_Forward_Domain_Invalid(t *testing.T) {
a := newTestApplication()
a.proxyConfig.CookieDomain = api.PtrString("foo")
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.ExternalHost = "https://test.goauthentik.io"
req, _ := http.NewRequest("GET", "/foo/bar/baz", nil)
rr := httptest.NewRecorder()
a.redirectToStart(rr, req)
assert.Equal(t, http.StatusFound, rr.Code)
loc, _ := rr.Result().Location()
assert.Equal(t, "https://test.goauthentik.io/akprox/start", loc.String())
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "https://test.goauthentik.io", s.Values[constants.SessionRedirect])
}
func TestRedirectToStart_Forward_Domain(t *testing.T) {
a := newTestApplication()
a.proxyConfig.CookieDomain = api.PtrString("goauthentik.io")
a.proxyConfig.Mode = api.PROXYMODE_FORWARD_DOMAIN.Ptr()
a.proxyConfig.ExternalHost = "https://test.goauthentik.io"
req, _ := http.NewRequest("GET", "/foo/bar/baz", nil)
rr := httptest.NewRecorder()
a.redirectToStart(rr, req)
assert.Equal(t, http.StatusFound, rr.Code)
loc, _ := rr.Result().Location()
assert.Equal(t, "https://test.goauthentik.io/akprox/start", loc.String())
s, _ := a.sessions.Get(req, constants.SeesionName)
assert.Equal(t, "https://test.goauthentik.io", s.Values[constants.SessionRedirect])
}

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/prometheus/client_golang/prometheus"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/proxyv2/application"
"goauthentik.io/internal/outpost/proxyv2/metrics"
"goauthentik.io/internal/utils/web"
@ -58,6 +59,9 @@ func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, str
var longestMatch *application.Application
longestMatchLength := 0
for _, app := range ps.apps {
if app.Mode() != api.PROXYMODE_FORWARD_DOMAIN {
continue
}
// Check if the cookie domain has a leading period for a wildcard
// This will decrease the weight of a wildcard domain, but a request to example.com
// with the cookie domain set to example.com will still be routed correctly.
@ -70,6 +74,11 @@ func (ps *ProxyServer) lookupApp(r *http.Request) (*application.Application, str
}
longestMatch = app
longestMatchLength = len(cd)
// Also for forward_auth_domain, we need to respond on the external domain
if app.ProxyConfig().ExternalHost == host {
ps.log.WithField("host", host).WithField("app", app.ProxyConfig().Name).Debug("Found app based on external_host")
return app, host
}
}
// Check if our longes match is 0, in which case we didn't match, so we
// manually return no app

View File

@ -46,7 +46,7 @@ func NewProxyServer(ac *ak.APIController, portOffset int) *ProxyServer {
rootMux.Use(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
h.ServeHTTP(rw, r)
rw.Header().Set("Server", "authentik_proxy2")
rw.Header().Set("X-Powered-By", "authentik_proxy2")
})
})
@ -91,7 +91,7 @@ func (ps *ProxyServer) TimerFlowCacheExpiry() {}
func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
app, ok := ps.apps[serverName]
if !ok {
ps.log.WithField("server-name", serverName).Debug("app does not exist")
ps.log.WithField("server-name", serverName).Debug("failed to get certificate for ServerName")
return nil
}
if app.Cert == nil {
@ -102,7 +102,11 @@ func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
}
func (ps *ProxyServer) getCertificates(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
appCert := ps.GetCertificate(info.ServerName)
sn := info.ServerName
if sn == "" {
return &ps.defaultCert, nil
}
appCert := ps.GetCertificate(sn)
if appCert == nil {
return &ps.defaultCert, nil
}
@ -151,17 +155,14 @@ func (ps *ProxyServer) Start() error {
wg.Add(3)
go func() {
defer wg.Done()
ps.log.Debug("Starting HTTP Server...")
ps.ServeHTTP()
}()
go func() {
defer wg.Done()
ps.log.Debug("Starting HTTPs Server...")
ps.ServeHTTPS()
}()
go func() {
defer wg.Done()
ps.log.Debug("Starting Metrics Server...")
metrics.RunServer()
}()
return nil

View File

@ -99,14 +99,14 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(responseLogger, req)
duration := float64(time.Since(t)) / float64(time.Millisecond)
h.afterHandler(h.logger.WithFields(log.Fields{
"remote": req.RemoteAddr,
"host": GetHost(req),
"request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method,
"size": responseLogger.Size(),
"status": responseLogger.Status(),
"upstream": responseLogger.upstream,
"request_useragent": req.UserAgent(),
"remote": req.RemoteAddr,
"host": GetHost(req),
"runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method,
"scheme": req.URL.Scheme,
"size": responseLogger.Size(),
"status": responseLogger.Status(),
"upstream": responseLogger.upstream,
"user_agent": req.UserAgent(),
}), req).Info(url.RequestURI())
}

View File

@ -1,6 +1,7 @@
package web
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httputil"
@ -24,6 +25,7 @@ func (ws *WebServer) configureProxy() {
if req.TLS != nil {
req.Header.Set("X-Forwarded-Proto", "https")
}
ws.log.WithField("url", req.URL.String()).WithField("headers", req.Header).Trace("tracing request to backend")
}
rp := &httputil.ReverseProxy{Director: director}
rp.ErrorHandler = ws.proxyErrorHandler
@ -65,15 +67,27 @@ func (ws *WebServer) configureProxy() {
}
func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) {
ws.log.Warning(err.Error())
ws.log.WithError(err).Warning("failed to proxy to backend")
rw.WriteHeader(http.StatusBadGateway)
_, err = rw.Write([]byte("authentik starting..."))
em := fmt.Sprintf("failed to connect to authentik backend: %v", err)
if !ws.p.IsRunning() {
em = "authentik starting..."
}
// return json if the client asks for json
if req.Header.Get("Accept") == "application/json" {
eem, _ := json.Marshal(map[string]string{
"error": em,
})
em = string(eem)
}
_, err = rw.Write([]byte(em))
if err != nil {
ws.log.WithError(err).Warning("failed to write error message")
}
}
func (ws *WebServer) proxyModifyResponse(r *http.Response) error {
r.Header.Set("server", "authentik")
r.Header.Set("X-Powered-By", "authentik")
r.Header.Del("Server")
return nil
}

View File

@ -16,6 +16,9 @@ func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Certif
ws.log.WithError(err).Error("failed to generate default cert")
}
return func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
if ch.ServerName == "" {
return &cert, nil
}
if ws.ProxyServer != nil {
appCert := ws.ProxyServer.GetCertificate(ch.ServerName)
if appCert != nil {

View File

@ -61,14 +61,15 @@ MODE_FILE="/tmp/authentik-mode"
if [[ "$1" == "server" ]]; then
wait_for_db
echo "server" > $MODE_FILE
# We only set prometheus_multiproc_dir for serer, as with the worker it just fills up the disk
# We only set PROMETHEUS_MULTIPROC_DIR for serer, as with the worker it just fills up the disk
# as one file is created per process
#
# Set to TMPDIR instead hardcoded path so this can be used outside docker too
export prometheus_multiproc_dir=$TMPDIR
export PROMETHEUS_MULTIPROC_DIR=$TMPDIR
python -m lifecycle.migrate
/authentik-proxy
elif [[ "$1" == "worker" ]]; then
wait_for_db
echo "worker" > $MODE_FILE
check_if_root "celery -A authentik.root.celery worker -Ofair --max-tasks-per-child=1 --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events"
elif [[ "$1" == "flower" ]]; then

View File

@ -1,13 +1,18 @@
"""Gunicorn config"""
import os
import pwd
from hashlib import sha512
from multiprocessing import cpu_count
import structlog
from kubernetes.config.incluster_config import SERVICE_HOST_ENV_NAME
from authentik import get_full_version
from authentik.lib.config import CONFIG
from authentik.lib.utils.http import get_http_session
from authentik.lib.utils.reflection import get_env
bind = "127.0.0.1:8000"
reload = True
try:
pwd.getpwnam("authentik")
@ -23,6 +28,9 @@ if os.path.exists("/dev/shm"): # nosec
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentik.root.settings")
max_requests = 1000
max_requests_jitter = 50
logconfig_dict = {
"version": 1,
"disable_existing_loggers": False,
@ -67,3 +75,31 @@ def worker_exit(server, worker):
from prometheus_client import multiprocess
multiprocess.mark_process_dead(worker.pid)
if not CONFIG.y_bool("disable_startup_analytics", False):
env = get_env()
should_send = env not in ["dev", "ci"]
if should_send:
try:
get_http_session().post(
"https://goauthentik.io/api/event",
json={
"domain": "authentik",
"name": "pageview",
"referrer": get_full_version(),
"url": (
f"http://localhost/{env}?utm_source={get_full_version()}&utm_medium={env}"
),
},
headers={
"User-Agent": sha512(str(CONFIG.y("secret_key")).encode("ascii")).hexdigest()[
:16
],
"Content-Type": "application/json",
},
timeout=5,
)
# pylint: disable=bare-except
except: # nosec
pass

View File

@ -26,9 +26,13 @@ def j_print(event: str, log_level: str = "info", **kwargs):
print(dumps(data), file=stderr)
j_print("Starting authentik bootstrap")
# Sanity check, ensure SECRET_KEY is set before we even check for database connectivity
if CONFIG.y("secret_key") is None or len(CONFIG.y("secret_key")) == 0:
j_print("----------------------------------------------------------------------")
j_print("Secret key missing, check https://goauthentik.io/docs/installation/.")
j_print("----------------------------------------------------------------------")
sysexit(1)
@ -45,7 +49,9 @@ while True:
break
except OperationalError as exc:
sleep(1)
j_print(f"PostgreSQL Connection failed, retrying... ({exc})")
j_print(f"PostgreSQL connection failed, retrying... ({exc})")
finally:
j_print("PostgreSQL connection successful")
REDIS_PROTOCOL_PREFIX = "redis://"
if CONFIG.y_bool("redis.tls", False):
@ -63,3 +69,7 @@ while True:
except RedisError as exc:
sleep(1)
j_print(f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL)
finally:
j_print("Redis Connection successful")
j_print("Finished authentik bootstrap")

Binary file not shown.

File diff suppressed because it is too large Load Diff

457
poetry.lock generated
View File

@ -54,11 +54,11 @@ vine = "5.0.0"
[[package]]
name = "asgiref"
version = "3.4.1"
version = "3.5.0"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
[package.extras]
tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
@ -163,11 +163,11 @@ visualize = ["graphviz (>0.5.1)", "Twisted (>=16.1.1)"]
[[package]]
name = "bandit"
version = "1.7.1"
version = "1.7.2"
description = "Security oriented static analyser for python code."
category = "dev"
optional = false
python-versions = ">=3.5"
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
@ -175,6 +175,11 @@ GitPython = ">=1.0.1"
PyYAML = ">=5.3.1"
stevedore = ">=1.20.0"
[package.extras]
test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"]
toml = ["toml"]
yaml = ["pyyaml"]
[[package]]
name = "bcrypt"
version = "3.2.0"
@ -201,40 +206,35 @@ python-versions = "*"
[[package]]
name = "black"
version = "21.12b0"
version = "22.1.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
click = ">=7.1.2"
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0,<1"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = ">=0.2.6,<2.0.0"
typing-extensions = [
{version = ">=3.10.0.0", markers = "python_version < \"3.10\""},
{version = "!=3.10.0.1", markers = "python_version >= \"3.10\""},
]
tomli = ">=1.1.0"
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
python2 = ["typed-ast (>=1.4.3)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "boto3"
version = "1.20.40"
version = "1.20.46"
description = "The AWS SDK for Python"
category = "main"
optional = false
python-versions = ">= 3.6"
[package.dependencies]
botocore = ">=1.23.40,<1.24.0"
botocore = ">=1.23.46,<1.24.0"
jmespath = ">=0.7.1,<1.0.0"
s3transfer = ">=0.5.0,<0.6.0"
@ -243,7 +243,7 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
version = "1.23.40"
version = "1.23.46"
description = "Low-level, data-driven core of boto 3."
category = "main"
optional = false
@ -267,11 +267,11 @@ python-versions = ">=3.5"
[[package]]
name = "cachetools"
version = "4.2.4"
version = "5.0.0"
description = "Extensible memoizing collections and decorators"
category = "main"
optional = false
python-versions = "~=3.5"
python-versions = "~=3.7"
[[package]]
name = "cattrs"
@ -490,11 +490,11 @@ python-versions = "*"
[[package]]
name = "coverage"
version = "6.2"
version = "6.3"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
[package.dependencies]
tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
@ -580,7 +580,7 @@ dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "im
[[package]]
name = "django"
version = "4.0.1"
version = "4.0.2"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
@ -844,14 +844,14 @@ gitdb = ">=4.0.1,<5"
[[package]]
name = "google-auth"
version = "2.3.3"
version = "2.4.0"
description = "Google Authentication Library"
category = "main"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
[package.dependencies]
cachetools = ">=2.0.0,<5.0"
cachetools = ">=2.0.0,<6.0"
pyasn1-modules = ">=0.2.1"
rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""}
six = ">=1.9.0"
@ -877,7 +877,7 @@ tornado = ["tornado (>=0.2)"]
[[package]]
name = "h11"
version = "0.12.0"
version = "0.13.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false
@ -1122,11 +1122,11 @@ python-versions = "*"
[[package]]
name = "multidict"
version = "5.2.0"
version = "6.0.1"
description = "multidict implementation"
category = "main"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.7"
[[package]]
name = "mypy-extensions"
@ -1297,7 +1297,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pycryptodome"
version = "3.12.0"
version = "3.14.0"
description = "Cryptographic library for Python"
category = "main"
optional = false
@ -1393,7 +1393,7 @@ test = ["flaky", "pretend", "pytest (>=3.0.1)"]
[[package]]
name = "pyparsing"
version = "3.0.6"
version = "3.0.7"
description = "Python parsing module"
category = "main"
optional = false
@ -1555,7 +1555,7 @@ test = ["fixtures", "mock", "purl", "pytest", "sphinx", "testrepository (>=0.0.1
[[package]]
name = "requests-oauthlib"
version = "1.3.0"
version = "1.3.1"
description = "OAuthlib authentication support for Requests."
category = "main"
optional = false
@ -1608,39 +1608,33 @@ urllib3 = {version = ">=1.26,<2.0", extras = ["secure"]}
[[package]]
name = "sentry-sdk"
version = "1.5.0"
description = ""
version = "1.5.4"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
develop = false
[package.dependencies]
certifi = "*"
urllib3 = ">=1.10.0"
[package.extras]
flask = ["flask (>=0.11)", "blinker (>=1.1)"]
bottle = ["bottle (>=0.12.13)"]
falcon = ["falcon (>=1.4)"]
django = ["django (>=1.8)"]
sanic = ["sanic (>=0.8)"]
celery = ["celery (>=3)"]
beam = ["apache-beam (>=2.12)"]
rq = ["rq (>=0.6)"]
aiohttp = ["aiohttp (>=3.5)"]
tornado = ["tornado (>=5)"]
sqlalchemy = ["sqlalchemy (>=1.2)"]
pyspark = ["pyspark (>=2.4.4)"]
pure_eval = ["pure-eval", "executing", "asttokens"]
beam = ["apache-beam (>=2.12)"]
bottle = ["bottle (>=0.12.13)"]
celery = ["celery (>=3)"]
chalice = ["chalice (>=1.16.0)"]
django = ["django (>=1.8)"]
falcon = ["falcon (>=1.4)"]
flask = ["flask (>=0.11)", "blinker (>=1.1)"]
httpx = ["httpx (>=0.16.0)"]
[package.source]
type = "git"
url = 'https://github.com/beryju/sentry-python.git'
reference = '379aee28b15d3b87b381317746c4efd24b3d7bc3'
resolved_reference = "379aee28b15d3b87b381317746c4efd24b3d7bc3"
pure_eval = ["pure-eval", "executing", "asttokens"]
pyspark = ["pyspark (>=2.4.4)"]
quart = ["quart (>=0.16.1)", "blinker (>=1.1)"]
rq = ["rq (>=0.6)"]
sanic = ["sanic (>=0.8)"]
sqlalchemy = ["sqlalchemy (>=1.2)"]
tornado = ["tornado (>=5)"]
[[package]]
name = "service-identity"
@ -1904,11 +1898,11 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "uvicorn"
version = "0.17.0"
version = "0.17.3"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
python-versions = "*"
python-versions = ">=3.7"
[package.dependencies]
asgiref = ">=3.4.0"
@ -2069,7 +2063,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "b943f64c1111790d5534be74e941ce6880efb75cdf562993a66f138c082cd80f"
content-hash = "d3061d973ae4fd3ddea0bff63f1dbd615322546024b75dc6015ef5faf01d6368"
[metadata.files]
aiohttp = [
@ -2159,8 +2153,8 @@ amqp = [
{file = "amqp-5.0.9.tar.gz", hash = "sha256:1e5f707424e544078ca196e72ae6a14887ce74e02bd126be54b7c03c971bef18"},
]
asgiref = [
{file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
{file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
{file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"},
{file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"},
]
asn1crypto = [
{file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"},
@ -2194,8 +2188,8 @@ automat = [
{file = "Automat-20.2.0.tar.gz", hash = "sha256:7979803c74610e11ef0c0d68a2942b152df52da55336e0c9d58daf1831cbdf33"},
]
bandit = [
{file = "bandit-1.7.1-py3-none-any.whl", hash = "sha256:f5acd838e59c038a159b5c621cf0f8270b279e884eadd7b782d7491c02add0d4"},
{file = "bandit-1.7.1.tar.gz", hash = "sha256:a81b00b5436e6880fa8ad6799bc830e02032047713cbb143a12939ac67eb756c"},
{file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"},
{file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"},
]
bcrypt = [
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd"},
@ -2214,24 +2208,45 @@ billiard = [
{file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
]
black = [
{file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"},
{file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"},
{file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"},
{file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"},
{file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"},
{file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"},
{file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"},
{file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"},
{file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"},
{file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"},
{file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"},
{file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"},
{file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"},
{file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"},
{file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"},
{file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"},
{file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"},
{file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"},
{file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"},
{file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"},
{file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"},
{file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"},
{file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"},
{file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"},
{file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"},
]
boto3 = [
{file = "boto3-1.20.40-py3-none-any.whl", hash = "sha256:cfe85589e4a0a997c7b9ae7432400b03fa6fa5fea29fdc48db3099a903b76998"},
{file = "boto3-1.20.40.tar.gz", hash = "sha256:66aef9a6d8cad393f69166112ba49e14e2c6766f9278c96134101314a9af2992"},
{file = "boto3-1.20.46-py3-none-any.whl", hash = "sha256:a2ffce001160d7e7c72a90c3084700d50eb64ea4a3aae8afe21566971d1fd611"},
{file = "boto3-1.20.46.tar.gz", hash = "sha256:d7effba509d7298ef49316ba2da7a2ea115f2a7ff691f875f6354666663cf386"},
]
botocore = [
{file = "botocore-1.23.40-py3-none-any.whl", hash = "sha256:88a314fe27cd97a0c731094c5b34db01ebe930801700e5d1b68485ebde746c3c"},
{file = "botocore-1.23.40.tar.gz", hash = "sha256:49baa1fca4483b24769f0743fbf72afe4db391f41f1fc12ea34e06036db642a4"},
{file = "botocore-1.23.46-py3-none-any.whl", hash = "sha256:354bce55e5adc8e2fe106acfd455ce448f9b920d7b697d06faa8cf200fd6566b"},
{file = "botocore-1.23.46.tar.gz", hash = "sha256:38dd4564839f531725b667db360ba7df2125ceb3752b0ba12759c3e918015b95"},
]
bump2version = [
{file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"},
{file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"},
]
cachetools = [
{file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"},
{file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"},
{file = "cachetools-5.0.0-py3-none-any.whl", hash = "sha256:8fecd4203a38af17928be7b90689d8083603073622229ca7077b72d8e5a976e4"},
{file = "cachetools-5.0.0.tar.gz", hash = "sha256:486471dfa8799eb7ec503a8059e263db000cdda20075ce5e48903087f79d5fd6"},
]
cattrs = [
{file = "cattrs-1.10.0-py3-none-any.whl", hash = "sha256:35dd9063244263e63bd0bd24ea61e3015b00272cead084b2c40d788b0f857c46"},
@ -2353,53 +2368,50 @@ constantly = [
{file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"},
]
coverage = [
{file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"},
{file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"},
{file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"},
{file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"},
{file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"},
{file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"},
{file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"},
{file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"},
{file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"},
{file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"},
{file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"},
{file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"},
{file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"},
{file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"},
{file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"},
{file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"},
{file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"},
{file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"},
{file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"},
{file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"},
{file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"},
{file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"},
{file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"},
{file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"},
{file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"},
{file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"},
{file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"},
{file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"},
{file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"},
{file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"},
{file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"},
{file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"},
{file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"},
{file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"},
{file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"},
{file = "coverage-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8071e7d9ba9f457fc674afc3de054450be2c9b195c470147fbbc082468d8ff7"},
{file = "coverage-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86c91c511853dfda81c2cf2360502cb72783f4b7cebabef27869f00cbe1db07d"},
{file = "coverage-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4ce3b647bd1792d4394f5690d9df6dc035b00bcdbc5595099c01282a59ae01"},
{file = "coverage-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a491e159294d756e7fc8462f98175e2d2225e4dbe062cca7d3e0d5a75ba6260"},
{file = "coverage-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d008e0f67ac800b0ca04d7914b8501312c8c6c00ad8c7ba17754609fae1231a"},
{file = "coverage-6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4578728c36de2801c1deb1c6b760d31883e62e33f33c7ba8f982e609dc95167d"},
{file = "coverage-6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7ee317486593193e066fc5e98ac0ce712178c21529a85c07b7cb978171f25d53"},
{file = "coverage-6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2bc85664b06ba42d14bb74d6ddf19d8bfc520cb660561d2d9ce5786ae72f71b5"},
{file = "coverage-6.3-cp310-cp310-win32.whl", hash = "sha256:27a94db5dc098c25048b0aca155f5fac674f2cf1b1736c5272ba28ead2fc267e"},
{file = "coverage-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bde4aeabc0d1b2e52c4036c54440b1ad05beeca8113f47aceb4998bb7471e2c2"},
{file = "coverage-6.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:509c68c3e2015022aeda03b003dd68fa19987cdcf64e9d4edc98db41cfc45d30"},
{file = "coverage-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4ff163602c5c77e7bb4ea81ba5d3b793b4419f8acd296aae149370902cf4e92"},
{file = "coverage-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1675db48490e5fa0b300f6329ecb8a9a37c29b9ab64fa9c964d34111788ca2d"},
{file = "coverage-6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7eed8459a2b81848cafb3280b39d7d49950d5f98e403677941c752e7e7ee47cb"},
{file = "coverage-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b4285fde5286b946835a1a53bba3ad41ef74285ba9e8013e14b5ea93deaeafc"},
{file = "coverage-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4748349734110fd32d46ff8897b561e6300d8989a494ad5a0a2e4f0ca974fc7"},
{file = "coverage-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:823f9325283dc9565ba0aa2d240471a93ca8999861779b2b6c7aded45b58ee0f"},
{file = "coverage-6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fff16a30fdf57b214778eff86391301c4509e327a65b877862f7c929f10a4253"},
{file = "coverage-6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1a428bdbe71f9a8c270c7baab29e9552ac9d0e0cba5e7e9a4c9ee6465d258d"},
{file = "coverage-6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7d82c610a2e10372e128023c5baf9ce3d270f3029fe7274ff5bc2897c68f1318"},
{file = "coverage-6.3-cp37-cp37m-win32.whl", hash = "sha256:11e61c5548ecf74ea1f8b059730b049871f0e32b74f88bd0d670c20c819ad749"},
{file = "coverage-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0c3525b1a182c8ffc9bca7e56b521e0c2b8b3e82f033c8e16d6d721f1b54d6"},
{file = "coverage-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a189036c50dcd56100746139a459f0d27540fef95b09aba03e786540b8feaa5f"},
{file = "coverage-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32168001f33025fd756884d56d01adebb34e6c8c0b3395ca8584cdcee9c7c9d2"},
{file = "coverage-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5d79c9af3f410a2b5acad91258b4ae179ee9c83897eb9de69151b179b0227f5"},
{file = "coverage-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85c5fc9029043cf8b07f73fbb0a7ab6d3b717510c3b5642b77058ea55d7cacde"},
{file = "coverage-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7596aa2f2b8fa5604129cfc9a27ad9beec0a96f18078cb424d029fdd707468d"},
{file = "coverage-6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ce443a3e6df90d692c38762f108fc4c88314bf477689f04de76b3f252e7a351c"},
{file = "coverage-6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:012157499ec4f135fc36cd2177e3d1a1840af9b236cbe80e9a5ccfc83d912a69"},
{file = "coverage-6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a34d313105cdd0d3644c56df2d743fe467270d6ab93b5d4a347eb9fec8924d6"},
{file = "coverage-6.3-cp38-cp38-win32.whl", hash = "sha256:6e78b1e25e5c5695dea012be473e442f7094d066925604be20b30713dbd47f89"},
{file = "coverage-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:433b99f7b0613bdcdc0b00cc3d39ed6d756797e3b078d2c43f8a38288520aec6"},
{file = "coverage-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ed3244b415725f08ca3bdf02ed681089fd95e9465099a21c8e2d9c5d6ca2606"},
{file = "coverage-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab4fc4b866b279740e0d917402f0e9a08683e002f43fa408e9655818ed392196"},
{file = "coverage-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8582e9280f8d0f38114fe95a92ae8d0790b56b099d728cc4f8a2e14b1c4a18c"},
{file = "coverage-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c72bb4679283c6737f452eeb9b2a0e570acaef2197ad255fb20162adc80bea76"},
{file = "coverage-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca29c352389ea27a24c79acd117abdd8a865c6eb01576b6f0990cd9a4e9c9f48"},
{file = "coverage-6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:152cc2624381df4e4e604e21bd8e95eb8059535f7b768c1fb8b8ae0b26f47ab0"},
{file = "coverage-6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:51372e24b1f7143ee2df6b45cff6a721f3abe93b1e506196f3ffa4155c2497f7"},
{file = "coverage-6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72d9d186508325a456475dd05b1756f9a204c7086b07fffb227ef8cee03b1dc2"},
{file = "coverage-6.3-cp39-cp39-win32.whl", hash = "sha256:649df3641eb351cdfd0d5533c92fc9df507b6b2bf48a7ef8c71ab63cbc7b5c3c"},
{file = "coverage-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:e67ccd53da5958ea1ec833a160b96357f90859c220a00150de011b787c27b98d"},
{file = "coverage-6.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:27ac7cb84538e278e07569ceaaa6f807a029dc194b1c819a9820b9bb5dbf63ab"},
{file = "coverage-6.3.tar.gz", hash = "sha256:987a84ff98a309994ca77ed3cc4b92424f824278e48e4bf7d1bb79a63cfe2099"},
]
cryptography = [
{file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"},
@ -2444,8 +2456,8 @@ deprecated = [
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
]
django = [
{file = "Django-4.0.1-py3-none-any.whl", hash = "sha256:7cd8e8a3ed2bc0dfda05ce1e53a9c81b30eefd7aa350e538a18884475e4d4ce2"},
{file = "Django-4.0.1.tar.gz", hash = "sha256:2485eea3cc4c3bae13080dee866ebf90ba9f98d1afe8fda89bfb0eb2e218ef86"},
{file = "Django-4.0.2-py3-none-any.whl", hash = "sha256:996495c58bff749232426c88726d8cd38d24c94d7c1d80835aafffa9bc52985a"},
{file = "Django-4.0.2.tar.gz", hash = "sha256:110fb58fb12eca59e072ad59fc42d771cd642dd7a2f2416582aa9da7a8ef954a"},
]
django-dbbackup = [
{file = "django-dbbackup-4.0.0b0.tar.gz", hash = "sha256:aca81265dab91b3a73c3a730cf168d946621c28002f1321d4c33910ba7c63634"},
@ -2581,16 +2593,16 @@ gitpython = [
{file = "GitPython-3.1.26.tar.gz", hash = "sha256:fc8868f63a2e6d268fb25f481995ba185a85a66fcad126f039323ff6635669ee"},
]
google-auth = [
{file = "google-auth-2.3.3.tar.gz", hash = "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e"},
{file = "google_auth-2.3.3-py2.py3-none-any.whl", hash = "sha256:a348a50b027679cb7dae98043ac8dbcc1d7951f06d8387496071a1e05a2465c0"},
{file = "google-auth-2.4.0.tar.gz", hash = "sha256:ef6f4827f6a3f9c5ff884616e2ba779acb5d690486fb70ca5e3091ed85ad932a"},
{file = "google_auth-2.4.0-py2.py3-none-any.whl", hash = "sha256:d1fad279d9d97e7d6b4a09a53e851ab2ee6d36d5c19547354a3f47a8a6ae41b9"},
]
gunicorn = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
{file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
{file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"},
{file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"},
]
hiredis = [
{file = "hiredis-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048"},
@ -2861,78 +2873,65 @@ msgpack = [
{file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"},
]
multidict = [
{file = "multidict-5.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3822c5894c72e3b35aae9909bef66ec83e44522faf767c0ad39e0e2de11d3b55"},
{file = "multidict-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:28e6d883acd8674887d7edc896b91751dc2d8e87fbdca8359591a13872799e4e"},
{file = "multidict-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b61f85101ef08cbbc37846ac0e43f027f7844f3fade9b7f6dd087178caedeee7"},
{file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9b668c065968c5979fe6b6fa6760bb6ab9aeb94b75b73c0a9c1acf6393ac3bf"},
{file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:517d75522b7b18a3385726b54a081afd425d4f41144a5399e5abd97ccafdf36b"},
{file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b4ac3ba7a97b35a5ccf34f41b5a8642a01d1e55454b699e5e8e7a99b5a3acf5"},
{file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df23c83398715b26ab09574217ca21e14694917a0c857e356fd39e1c64f8283f"},
{file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e58a9b5cc96e014ddf93c2227cbdeca94b56a7eb77300205d6e4001805391747"},
{file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f76440e480c3b2ca7f843ff8a48dc82446b86ed4930552d736c0bac507498a52"},
{file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cfde464ca4af42a629648c0b0d79b8f295cf5b695412451716531d6916461628"},
{file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0fed465af2e0eb6357ba95795d003ac0bdb546305cc2366b1fc8f0ad67cc3fda"},
{file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b70913cbf2e14275013be98a06ef4b412329fe7b4f83d64eb70dce8269ed1e1a"},
{file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5635bcf1b75f0f6ef3c8a1ad07b500104a971e38d3683167b9454cb6465ac86"},
{file = "multidict-5.2.0-cp310-cp310-win32.whl", hash = "sha256:77f0fb7200cc7dedda7a60912f2059086e29ff67cefbc58d2506638c1a9132d7"},
{file = "multidict-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:9416cf11bcd73c861267e88aea71e9fcc35302b3943e45e1dbb4317f91a4b34f"},
{file = "multidict-5.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd77c8f3cba815aa69cb97ee2b2ef385c7c12ada9c734b0f3b32e26bb88bbf1d"},
{file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ec9aea6223adf46999f22e2c0ab6cf33f5914be604a404f658386a8f1fba37"},
{file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5283c0a00f48e8cafcecadebfa0ed1dac8b39e295c7248c44c665c16dc1138b"},
{file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5f79c19c6420962eb17c7e48878a03053b7ccd7b69f389d5831c0a4a7f1ac0a1"},
{file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4a67f1080123de76e4e97a18d10350df6a7182e243312426d508712e99988d4"},
{file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:94b117e27efd8e08b4046c57461d5a114d26b40824995a2eb58372b94f9fca02"},
{file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2e77282fd1d677c313ffcaddfec236bf23f273c4fba7cdf198108f5940ae10f5"},
{file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:116347c63ba049c1ea56e157fa8aa6edaf5e92925c9b64f3da7769bdfa012858"},
{file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:dc3a866cf6c13d59a01878cd806f219340f3e82eed514485e094321f24900677"},
{file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac42181292099d91217a82e3fa3ce0e0ddf3a74fd891b7c2b347a7f5aa0edded"},
{file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f0bb0973f42ffcb5e3537548e0767079420aefd94ba990b61cf7bb8d47f4916d"},
{file = "multidict-5.2.0-cp36-cp36m-win32.whl", hash = "sha256:ea21d4d5104b4f840b91d9dc8cbc832aba9612121eaba503e54eaab1ad140eb9"},
{file = "multidict-5.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e6453f3cbeb78440747096f239d282cc57a2997a16b5197c9bc839099e1633d0"},
{file = "multidict-5.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3def943bfd5f1c47d51fd324df1e806d8da1f8e105cc7f1c76a1daf0f7e17b0"},
{file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35591729668a303a02b06e8dba0eb8140c4a1bfd4c4b3209a436a02a5ac1de11"},
{file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8cacda0b679ebc25624d5de66c705bc53dcc7c6f02a7fb0f3ca5e227d80422"},
{file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:baf1856fab8212bf35230c019cde7c641887e3fc08cadd39d32a421a30151ea3"},
{file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a43616aec0f0d53c411582c451f5d3e1123a68cc7b3475d6f7d97a626f8ff90d"},
{file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25cbd39a9029b409167aa0a20d8a17f502d43f2efebfe9e3ac019fe6796c59ac"},
{file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a2cbcfbea6dc776782a444db819c8b78afe4db597211298dd8b2222f73e9cd0"},
{file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d2d7d1fff8e09d99354c04c3fd5b560fb04639fd45926b34e27cfdec678a704"},
{file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a37e9a68349f6abe24130846e2f1d2e38f7ddab30b81b754e5a1fde32f782b23"},
{file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:637c1896497ff19e1ee27c1c2c2ddaa9f2d134bbb5e0c52254361ea20486418d"},
{file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9815765f9dcda04921ba467957be543423e5ec6a1136135d84f2ae092c50d87b"},
{file = "multidict-5.2.0-cp37-cp37m-win32.whl", hash = "sha256:8b911d74acdc1fe2941e59b4f1a278a330e9c34c6c8ca1ee21264c51ec9b67ef"},
{file = "multidict-5.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:380b868f55f63d048a25931a1632818f90e4be71d2081c2338fcf656d299949a"},
{file = "multidict-5.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e7d81ce5744757d2f05fc41896e3b2ae0458464b14b5a2c1e87a6a9d69aefaa8"},
{file = "multidict-5.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d1d55cdf706ddc62822d394d1df53573d32a7a07d4f099470d3cb9323b721b6"},
{file = "multidict-5.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4771d0d0ac9d9fe9e24e33bed482a13dfc1256d008d101485fe460359476065"},
{file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7d57ea65744d249427793c042094c4016789eb2562576fb831870f9c878d9e"},
{file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdd68778f96216596218b4e8882944d24a634d984ee1a5a049b300377878fa7c"},
{file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecc99bce8ee42dcad15848c7885197d26841cb24fa2ee6e89d23b8993c871c64"},
{file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:067150fad08e6f2dd91a650c7a49ba65085303fcc3decbd64a57dc13a2733031"},
{file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78c106b2b506b4d895ddc801ff509f941119394b89c9115580014127414e6c2d"},
{file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6c4fa1ec16e01e292315ba76eb1d012c025b99d22896bd14a66628b245e3e01"},
{file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b227345e4186809d31f22087d0265655114af7cda442ecaf72246275865bebe4"},
{file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:06560fbdcf22c9387100979e65b26fba0816c162b888cb65b845d3def7a54c9b"},
{file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7878b61c867fb2df7a95e44b316f88d5a3742390c99dfba6c557a21b30180cac"},
{file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:246145bff76cc4b19310f0ad28bd0769b940c2a49fc601b86bfd150cbd72bb22"},
{file = "multidict-5.2.0-cp38-cp38-win32.whl", hash = "sha256:c30ac9f562106cd9e8071c23949a067b10211917fdcb75b4718cf5775356a940"},
{file = "multidict-5.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:f19001e790013ed580abfde2a4465388950728861b52f0da73e8e8a9418533c0"},
{file = "multidict-5.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c1ff762e2ee126e6f1258650ac641e2b8e1f3d927a925aafcfde943b77a36d24"},
{file = "multidict-5.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd6c9c50bf2ad3f0448edaa1a3b55b2e6866ef8feca5d8dbec10ec7c94371d21"},
{file = "multidict-5.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc66d4016f6e50ed36fb39cd287a3878ffcebfa90008535c62e0e90a7ab713ae"},
{file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9acb76d5f3dd9421874923da2ed1e76041cb51b9337fd7f507edde1d86535d6"},
{file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dfc924a7e946dd3c6360e50e8f750d51e3ef5395c95dc054bc9eab0f70df4f9c"},
{file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fdba7333eb2351fee2596b756d730d62b5827d5e1ab2f84e6cbb287cc67fe0"},
{file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b9aad49466b8d828b96b9e3630006234879c8d3e2b0a9d99219b3121bc5cdb17"},
{file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:93de39267c4c676c9ebb2057e98a8138bade0d806aad4d864322eee0803140a0"},
{file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9bef5cff994ca3026fcc90680e326d1a19df9841c5e3d224076407cc21471a1"},
{file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5f841c4f14331fd1e36cbf3336ed7be2cb2a8f110ce40ea253e5573387db7621"},
{file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:38ba256ee9b310da6a1a0f013ef4e422fca30a685bcbec86a969bd520504e341"},
{file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3bc3b1621b979621cee9f7b09f024ec76ec03cc365e638126a056317470bde1b"},
{file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ee908c070020d682e9b42c8f621e8bb10c767d04416e2ebe44e37d0f44d9ad5"},
{file = "multidict-5.2.0-cp39-cp39-win32.whl", hash = "sha256:1c7976cd1c157fa7ba5456ae5d31ccdf1479680dc9b8d8aa28afabc370df42b8"},
{file = "multidict-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c9631c642e08b9fff1c6255487e62971d8b8e821808ddd013d8ac058087591ac"},
{file = "multidict-5.2.0.tar.gz", hash = "sha256:0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"},
{file = "multidict-6.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05c3cca447fd39b566615d7cf918f0e83cd92f0549f8182a6cab6e4729c02566"},
{file = "multidict-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b487ad10f594e78e0e61662ff7643e1adc610919ea8a95dd976126bffc0256f"},
{file = "multidict-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d65b5bd50a5e1f4c6427ebcabb5acc62540301de733f81fd549cf7633826fccd"},
{file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a542ceca143e19c08715defe133475961d43d7bfb48dbdc3ac506548ec168f18"},
{file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af441cfeb3003d96de8acdeb402187bc6a34f96e125618b8275bacfd29a6fad7"},
{file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f97f03d7dbc33e20a73428be93974e82762b393579d59053da1257c35bad13b"},
{file = "multidict-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c74c7a058a16b7dd24d030f0e8fe846f7d63f41cfeea89a804969cb129182a8"},
{file = "multidict-6.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5210e5eca8f0fc0213da31cbc29587d15195b21f40ffd53ff513126c39be0e2"},
{file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:af1fdc8d25eb9e9998854e996bc982ae3be733590a9e8d6b179b28fd212ddf71"},
{file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf0aaaef2f8a4cf1973ebe17162b0c3016859eb706d55b328993b8fd8c3515c"},
{file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:1354127c9687d0abbaadfb26e35db247ef1c35f0271e8052afa98b34a9140445"},
{file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2e82ac157bc0719b9802578964d4387e0d7dd530d1fd8949f3cef1c97f2e9d8a"},
{file = "multidict-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f90eb274732fe0958f2d9d4cc7a608bf218e63dd554a690ccafa30f9d9d1b2d"},
{file = "multidict-6.0.1-cp310-cp310-win32.whl", hash = "sha256:593586d7ac76ab7a9f229c49d81136b8c1a78119d948ba2a672f4fa681ad54ce"},
{file = "multidict-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:f24b3d4405271d6d1b920261437f3acb7dd1c329b0ac782b8b4e0ee7ad187e5b"},
{file = "multidict-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9050a2487f9dee9b0abde090b2620a3ba57190d7fe1dc2932b32b9866f90df9e"},
{file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff8004ecfb2cc00cd4f063c5e8b3cfd7e544bf774f17c2a861067444532e4fbc"},
{file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b944477c1e7be8bea2ef819157fb01b79493af1a6dba83d315be63db2957af6"},
{file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70a5fff576fe9039550f8d6aacaa98ce26c41df97edc577688c563eb25bcbe83"},
{file = "multidict-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e545e5922a4ae99e680a96ed88d03007bd4208fc2599ff273eb2086051d3ef74"},
{file = "multidict-6.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502c1610c7737697108ba4717274cca01130ce3e23227034b1601fb9fa0a8aa"},
{file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:caa9f2874519e0fbb19b90380532e59ef9e07d8ed22d8f0e2e69af796bae2c78"},
{file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7c9213754993e0dcf70a4fffebef502daf9323fa8d56f0485f724bd4c2991667"},
{file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9857789fd76e0394a5373598820bb2ea3dd113e2c0fb9a92248e3dadd81c5c"},
{file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:92ba0e4745937efef33399348f62a2dc98505dde4d0764061a34123a55fe02e9"},
{file = "multidict-6.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3424df56debab711f29c965cadb835dc3c702930265eae26f15ac784feb1dddd"},
{file = "multidict-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:d13af99bf380567ede69927d3188cfaccfde76e78e336151e42e60fe03ca5c00"},
{file = "multidict-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:37228376057f37fb013130e83e7ad0921598760fb75bd8f0da17403390241d20"},
{file = "multidict-6.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b469534be490ded141d74d7a158db284055b0d04991c2634a9d26d906e063dd"},
{file = "multidict-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6ee83096d27dfe52075385f5067d2a54fb227b41666b207e6a64f98ce9048266"},
{file = "multidict-6.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19c9c56ddca200f7ffbf376cf2d1a370e8f2e306ec16510a4d90b78a1a0be62"},
{file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a6da1575300e24011352c7c620d19b072a2c9498429d1f587e5090621e6f568"},
{file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48e0919ed5cc5e58c68f40b6516e3f1b0837580a7a8d51d9f99bf0f415d0b73a"},
{file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7c10ab3a1ad3188d9a4be01a10c0ddb67d6feaaea538781e73d6ba69fcafbbd"},
{file = "multidict-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17289c1ad70e1104ea25f560f6e2941718112d59616482589646aa01fcf4d0d1"},
{file = "multidict-6.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a701e9ad52e47b740b5b9aba627ac8fc4ee9e68682f0228fb4a7c4562631ffac"},
{file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:798dd2d825ce6c0699ce261fda90020562236480192604cfc5be4a315f806810"},
{file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:564ff7d88b4965cb5211ebf785bca409abbaac8dcca62b0fabe39d56a5ee7283"},
{file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:39d5424379505110d6ca64e927f8b2772c57b05a3d240e66805244ecc4402311"},
{file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:efeba14caaacb4a202d977aca63866069e8a57e632282caf971ce8ff472a49cd"},
{file = "multidict-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:241071469989c87da1835f3ccc4143a207b99a2ca27c19b7130a5edecc41a39e"},
{file = "multidict-6.0.1-cp38-cp38-win32.whl", hash = "sha256:7184dae6d519a8e629ef10e0e215dde022ee38d55254bea381ca0e61b8b197ea"},
{file = "multidict-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:666f78daccf8133ebfacb77e81f4077570b03641b49555ebb5e75797b72770e1"},
{file = "multidict-6.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a4fcf74d50a65fe99a68531d09f10ceef911992e0e64c1b2cf212effa075f8b"},
{file = "multidict-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a98ee92e96157a3c787c5aa156968ceffb6fb4548c5f74ff879884fed2f9752"},
{file = "multidict-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6bfb0a4df79bd78907fff46c34ed47284d5eb459b096d2de896b0b1c31f3fa7"},
{file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbda7f3be5a75ee87a2649382e47f419374420ef244b372312e662637931399f"},
{file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f74a680ba9d6c543c1cdd8bcef464d58b933bed29edb89a0b3c0b33cc553181a"},
{file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:389a689aa3b657b87cb21f78468caa4dfccd758639ff4b37b6e2dceef7c5b12f"},
{file = "multidict-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36195396f2a76dd23a67ca2bdeb8589b6eaaef7e84c97cf90da3ede69f189baa"},
{file = "multidict-6.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02847aa3602e21c04ac6fdef6f7c6dc916de8cce49a9eb59aeffedd3365f196c"},
{file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bd21540c9cc9b90373b5a6aaed8f8d53815529e89210453e0566d00389034b82"},
{file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e037782357ddf6741023df1a84f17a3a1bb62abba52e41efa4014199f6338143"},
{file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ea08fb7383422148bcfc066c3d2dae03fcdb8bd06e7ce713badd86bc26c25179"},
{file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b85354b2d2fe38a3ab90348f8c9778a437d376252f62115cabea1106332bd1b6"},
{file = "multidict-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c863a506466d64871c70e4204cecdcaf2c0c92d157683bb5d5b3e7b5aaea05a"},
{file = "multidict-6.0.1-cp39-cp39-win32.whl", hash = "sha256:d2cc15d2507b0d5f09726fd97bb9ffaa6bb47487fe77a208262cb6187fd9dcf3"},
{file = "multidict-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:38ada537db7f9089560cf16dc5b8b280096213bc5260970929aca43675682739"},
{file = "multidict-6.0.1.tar.gz", hash = "sha256:d40616f3f9326a18e1f2fa7c7e8e0e04a7e5228bfa2bd62c1e0d68fbc259b09b"},
]
mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
@ -3075,36 +3074,36 @@ pycparser = [
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
pycryptodome = [
{file = "pycryptodome-3.12.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:90ad3381ccdc6a24cc2841e295706a168f32abefe64c679695712acac71fd5da"},
{file = "pycryptodome-3.12.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e80f7469b0b3ea0f694230477d8501dc5a30a717e94fddd4821e6721f3053eae"},
{file = "pycryptodome-3.12.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b91404611767a7485837a6f1fd20cf9a5ae0ad362040a022cd65827ecb1b0d00"},
{file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:db66ccda65d5d20c17b00768e462a86f6f540f9aea8419a7f76cc7d9effd82cd"},
{file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:dc88355c4b261ed259268e65705b28b44d99570337694d593f06e3b1698eaaf3"},
{file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:6f8f5b7b53516da7511951910ab458e799173722c91fea54e2ba2f56d102e4aa"},
{file = "pycryptodome-3.12.0-cp27-cp27m-win32.whl", hash = "sha256:93acad54a72d81253242eb0a15064be559ec9d989e5173286dc21cad19f01765"},
{file = "pycryptodome-3.12.0-cp27-cp27m-win_amd64.whl", hash = "sha256:5a8c24d39d4a237dbfe181ea6593792bf9b5582c7fcfa7b8e0e12fda5eec07af"},
{file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:32d15da81959faea6cbed95df2bb44f7f796211c110cf90b5ad3b2aeeb97fc8e"},
{file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:aed7eb4b64c600fbc5e6d4238991ad1b4179a558401f203d1fcbd24883748982"},
{file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:341c6bbf932c406b4f3ee2372e8589b67ac0cf4e99e7dc081440f43a3cde9f0f"},
{file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:de0b711d673904dd6c65307ead36cb76622365a393569bf880895cba21195b7a"},
{file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3558616f45d8584aee3eba27559bc6fd0ba9be6c076610ed3cc62bd5229ffdc3"},
{file = "pycryptodome-3.12.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a78e4324e566b5fbc2b51e9240950d82fa9e1c7eb77acdf27f58712f65622c1d"},
{file = "pycryptodome-3.12.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:3f2f3dd596c6128d91314e60a6bcf4344610ef0e97f4ae4dd1770f86dd0748d8"},
{file = "pycryptodome-3.12.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e05f994f30f1cda3cbe57441f41220d16731cf99d868bb02a8f6484c454c206b"},
{file = "pycryptodome-3.12.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:4cded12e13785bbdf4ba1ff5fb9d261cd98162145f869e4fbc4a4b9083392f0b"},
{file = "pycryptodome-3.12.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:1181c90d1a6aee68a84826825548d0db1b58d8541101f908d779d601d1690586"},
{file = "pycryptodome-3.12.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6bb0d340c93bcb674ea8899e2f6408ec64c6c21731a59481332b4b2a8143cc60"},
{file = "pycryptodome-3.12.0-cp35-abi3-win32.whl", hash = "sha256:39da5807aa1ff820799c928f745f89432908bf6624b9e981d2d7f9e55d91b860"},
{file = "pycryptodome-3.12.0-cp35-abi3-win_amd64.whl", hash = "sha256:212c7f7fe11cad9275fbcff50ca977f1c6643f13560d081e7b0f70596df447b8"},
{file = "pycryptodome-3.12.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:b07a4238465eb8c65dd5df2ab8ba6df127e412293c0ed7656c003336f557a100"},
{file = "pycryptodome-3.12.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:a6e1bcd9d5855f1a3c0f8d585f44c81b08f39a02754007f374fb8db9605ba29c"},
{file = "pycryptodome-3.12.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:aceb1d217c3a025fb963849071446cf3aca1353282fe1c3cb7bd7339a4d47947"},
{file = "pycryptodome-3.12.0-pp27-pypy_73-win32.whl", hash = "sha256:f699360ae285fcae9c8f53ca6acf33796025a82bb0ccd7c1c551b04c1726def3"},
{file = "pycryptodome-3.12.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d845c587ceb82ac7cbac7d0bf8c62a1a0fe7190b028b322da5ca65f6e5a18b9e"},
{file = "pycryptodome-3.12.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:d8083de50f6dec56c3c6f270fb193590999583a1b27c9c75bc0b5cac22d438cc"},
{file = "pycryptodome-3.12.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:9ea2f6674c803602a7c0437fccdc2ea036707e60456974fe26ca263bd501ec45"},
{file = "pycryptodome-3.12.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:5d4264039a2087977f50072aaff2346d1c1c101cb359f9444cf92e3d1f42b4cd"},
{file = "pycryptodome-3.12.0.zip", hash = "sha256:12c7343aec5a3b3df5c47265281b12b611f26ec9367b6129199d67da54b768c1"},
{file = "pycryptodome-3.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bd800856e6dea6924504795ae4ec0d822e912e0a9a215e73b77b585c4d15a0f7"},
{file = "pycryptodome-3.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:625f78ad69aa3c45e19b85b9e9cae3a30aa4a1de6b908981a63426b88e860489"},
{file = "pycryptodome-3.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:a1c116dd7a00aac631f67920912fd8ef7a5ad3402cd4d497c6f5cc6b8115747b"},
{file = "pycryptodome-3.14.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0d0b6cca6b707b2c7cd4177c2d3cd950efa959ed8f01c30e676f102c68156f00"},
{file = "pycryptodome-3.14.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d939a257117cc8c6840ad69f149b3ca5e07268cfe0429bd9feec0f91da2343d"},
{file = "pycryptodome-3.14.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:41dbb8c2129d43f34ed555cbd365d5e8f023ef0f9238fd9cd0302086b15a38b3"},
{file = "pycryptodome-3.14.0-cp27-cp27m-win32.whl", hash = "sha256:9b454af09914807cef1222d100a8c523737a160347cb8d699facc4bdfb9fe725"},
{file = "pycryptodome-3.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:95bac6e55411650933f3b615e57bf0966bf08f3ce07c01f07482ced95f18cbec"},
{file = "pycryptodome-3.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:0ffbca43c1788243421a8583d85acb59f4cd0b82b001c485fdc3fbfd8fd0804f"},
{file = "pycryptodome-3.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:69b85d78f7db628370d2cc87f1c41a449f6460896ba95f412173618f75027c2c"},
{file = "pycryptodome-3.14.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:bba348d2823315ab8ebe44f0b2fc2ff8dfac8de881713a08def3dadcfc8e92bb"},
{file = "pycryptodome-3.14.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7d667daa851b1f9a20f2b5cad3cff13fba5204bc2f857d12f27c25b178d8629b"},
{file = "pycryptodome-3.14.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:74918d5de06b12fef2255135bede41307a5f7b929b145ad867111525aea075dc"},
{file = "pycryptodome-3.14.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c2b6faabd09d2876f9050f8af5d78046d81fe856f99e801c2ddab85b59602007"},
{file = "pycryptodome-3.14.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:22a8629315c76d2bec57bc4fd67eb7e01664c3e3b9579df40f530ee5821db1de"},
{file = "pycryptodome-3.14.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:7e3851e4fbbab72d9b30f98a504f450cc61e497e8e4b0be8205dc198703eee4d"},
{file = "pycryptodome-3.14.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:9006f17944efaacc3be364c01c2253c00a00f0b5fa5a1a85a1191efd861e764d"},
{file = "pycryptodome-3.14.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:8f0da308fca149b4c4da78e1388f82d8dd167e0ce12992a44f81b506cede3109"},
{file = "pycryptodome-3.14.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:d186e34747985fbd94df7ed4d621f8377165053a06872314c2a594af34741655"},
{file = "pycryptodome-3.14.0-cp35-abi3-win32.whl", hash = "sha256:2ed4da8f8afe44895c1f49ae1141a55b15d81dc745b5baa7b7a7265d7b40b81e"},
{file = "pycryptodome-3.14.0-cp35-abi3-win_amd64.whl", hash = "sha256:11167a1f892283e5320feb5e81589fd041a1822b94c047820f00bc03eb98a9f7"},
{file = "pycryptodome-3.14.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:1714ea5f83bcff25e8ae4640e22359d7a0815157a29d9f4eebc2b9e975a3cda0"},
{file = "pycryptodome-3.14.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:3a011b9fe674bd21056613e88a3e660c56f1b47263138ebf420aa3ee4b8b0107"},
{file = "pycryptodome-3.14.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:3fd50e3682ac3a684ace5b90ba1aef8090a78eeadf38c1ec385aad3a599cfd68"},
{file = "pycryptodome-3.14.0-pp27-pypy_73-win32.whl", hash = "sha256:08be50d4195edd595df580077bbeec5599d0e5aa0cc468083178ae870e0b29f4"},
{file = "pycryptodome-3.14.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:16c171dd969c9046b7b304c6ba0c643624dcf18093a66bd30b8b091703f177a2"},
{file = "pycryptodome-3.14.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:89bb56cfd1fb74663842710bc41a6be26dafceb60eb8d432536891aea08a3740"},
{file = "pycryptodome-3.14.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c30a98c8718ae93d44680a7038adb484a520319860747ba43b6cd0a20f6b5984"},
{file = "pycryptodome-3.14.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:e972f566ef7b821c8b958dab64174afa072f8271b779e32444ad7c127b0a84b2"},
{file = "pycryptodome-3.14.0.tar.gz", hash = "sha256:ceea92a4b8ba6c50d8d70f2efbb4ea14b002dac4160ce4dda33f1b7442f8158a"},
]
pyjwt = [
{file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"},
@ -3139,8 +3138,8 @@ pyopenssl = [
{file = "pyOpenSSL-21.0.0.tar.gz", hash = "sha256:5e2d8c5e46d0d865ae933bef5230090bdaf5506281e9eec60fa250ee80600cb3"},
]
pyparsing = [
{file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
{file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pyrsistent = [
{file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"},
@ -3251,9 +3250,8 @@ requests-mock = [
{file = "requests_mock-1.9.3-py2.py3-none-any.whl", hash = "sha256:0a2d38a117c08bb78939ec163522976ad59a6b7fdd82b709e23bb98004a44970"},
]
requests-oauthlib = [
{file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
{file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
{file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
{file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
{file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
]
rsa = [
{file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"},
@ -3266,7 +3264,10 @@ s3transfer = [
selenium = [
{file = "selenium-4.1.0-py3-none-any.whl", hash = "sha256:27e7b64df961d609f3d57237caa0df123abbbe22d038f2ec9e332fb90ec1a939"},
]
sentry-sdk = []
sentry-sdk = [
{file = "sentry-sdk-1.5.4.tar.gz", hash = "sha256:f7e54567937ebcbe938c4df1075ec891587faeb7c74184b88cf2894e47c86116"},
{file = "sentry_sdk-1.5.4-py2.py3-none-any.whl", hash = "sha256:4fc7960a82c95d906a0514cf4d9aacba1743eb9863a5b7c2a01c525a7d9b21e6"},
]
service-identity = [
{file = "service-identity-21.1.0.tar.gz", hash = "sha256:6e6c6086ca271dc11b033d17c3a8bea9f24ebff920c587da090afc9519419d34"},
{file = "service_identity-21.1.0-py2.py3-none-any.whl", hash = "sha256:f0b0caac3d40627c3c04d7a51b6e06721857a0e10a8775f2d1d7e72901b3a7db"},
@ -3405,8 +3406,8 @@ urllib3 = [
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
uvicorn = [
{file = "uvicorn-0.17.0-py3-none-any.whl", hash = "sha256:0b89c91bb8fe84c4bded9996af13c4b8c0de799d29bffeaa0c8ad298f2be0934"},
{file = "uvicorn-0.17.0.tar.gz", hash = "sha256:192c2422b056a3beb512c6c260bf77a7a884204a4ae41856719c1913ead63bbb"},
{file = "uvicorn-0.17.3-py3-none-any.whl", hash = "sha256:3ab1bf48aa512692db93a91c514576a0739a9d3522827e1656a172ee87118fa5"},
{file = "uvicorn-0.17.3.tar.gz", hash = "sha256:3cebddac78c7dd6bfce2f8f838c2c9b0cfcf3dddf417315ba62378ebe7e8fae2"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},

View File

@ -14,7 +14,7 @@ pythonPlatform = "Linux"
[tool.black]
line-length = 100
target-version = ['py39']
target-version = ['py310']
exclude = 'node_modules'
[tool.isort]
@ -92,7 +92,7 @@ addopts = "-p no:celery --junitxml=unittest.xml"
[tool.poetry]
name = "authentik"
version = "2022.1.1"
version = "2022.1.5"
description = ""
authors = ["Jens Langhammer <jens.langhammer@beryju.org>"]
@ -135,7 +135,7 @@ pyjwt = "*"
python = "^3.10"
pyyaml = "*"
requests-oauthlib = "*"
sentry-sdk = { git = 'https://github.com/beryju/sentry-python.git', rev = '379aee28b15d3b87b381317746c4efd24b3d7bc3' }
sentry-sdk = "*"
service_identity = "*"
structlog = "*"
swagger-spec-validator = "*"

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2022.1.1
version: 2022.1.5
description: Making authentication simple.
contact:
email: hello@beryju.org

View File

@ -3,7 +3,11 @@ from authentik.lib.generators import generate_id
from yaml import safe_dump
with open("local.env.yml", "w") as _config:
safe_dump({
"log_level": "debug",
"secret_key": generate_id(),
}, _config, default_flow_style=False)
safe_dump(
{
"log_level": "debug",
"secret_key": generate_id(),
},
_config,
default_flow_style=False,
)

View File

@ -13,7 +13,10 @@ if os.environ.get(env_pr_branch, "") != "":
should_build = str(os.environ.get("DOCKER_USERNAME", "") != "").lower()
print("##[set-output name=branchName]%s" % branch_name)
print("##[set-output name=branchNameContainer]%s" % branch_name.replace("refs/heads/", "").replace("/", "-"))
print(
"##[set-output name=branchNameContainer]%s"
% branch_name.replace("refs/heads/", "").replace("/", "-")
)
print("##[set-output name=timestamp]%s" % int(time()))
print("##[set-output name=sha]%s" % os.environ[sha])
print("##[set-output name=shouldBuild]%s" % should_build)

View File

@ -2,20 +2,15 @@
from json import loads, dumps
TSCONFIG_ESM = {
"compilerOptions": {
"declaration": True,
"target": "es6",
"module": "esnext",
"moduleResolution": "node",
"outDir": "./dist/esm",
"typeRoots": [
"node_modules/@types"
]
},
"exclude": [
"dist",
"node_modules"
]
"compilerOptions": {
"declaration": True,
"target": "es6",
"module": "esnext",
"moduleResolution": "node",
"outDir": "./dist/esm",
"typeRoots": ["node_modules/@types"],
},
"exclude": ["dist", "node_modules"],
}
@ -24,7 +19,7 @@ with open("web-api/package.json", encoding="utf-8") as _package:
package["license"] = "GPL-3.0-only"
package["module"] = "./dist/esm/index.js"
package["sideEffects"] = False
package["scripts"]["build"] = "tsc && tsc --project tsconfig.esm.json"
package["scripts"]["build"] = "tsc && tsc --project tsconfig.esm.json"
open("web-api/package.json", "w+", encoding="utf-8").write(dumps(package))
open("web-api/tsconfig.esm.json", "w+", encoding="utf-8").write(dumps(TSCONFIG_ESM))

1025
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,9 @@
"en",
"pseudo-LOCALE",
"fr_FR",
"tr"
"tr",
"es",
"pl"
],
"formatOptions": {
"lineNumbers": false
@ -46,20 +48,20 @@
]
},
"dependencies": {
"@babel/core": "^7.16.10",
"@babel/core": "^7.16.12",
"@babel/plugin-proposal-decorators": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.16.10",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@formatjs/intl-listformat": "^6.5.0",
"@formatjs/intl-listformat": "^6.5.1",
"@fortawesome/fontawesome-free": "^5.15.4",
"@goauthentik/api": "^2021.12.5-1642024690",
"@goauthentik/api": "^2022.1.3-1643236150",
"@jackfranklin/rollup-plugin-markdown": "^0.3.0",
"@lingui/cli": "^3.13.0",
"@lingui/core": "^3.13.0",
"@lingui/detect-locale": "^3.13.0",
"@lingui/macro": "^3.13.0",
"@patternfly/patternfly": "^4.164.2",
"@lingui/cli": "^3.13.2",
"@lingui/core": "^3.13.2",
"@lingui/detect-locale": "^3.13.2",
"@lingui/macro": "^3.13.2",
"@patternfly/patternfly": "^4.171.1",
"@polymer/iron-form": "^3.0.1",
"@polymer/paper-input": "^3.2.1",
"@rollup/plugin-babel": "^5.3.0",
@ -67,34 +69,34 @@
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-replace": "^3.0.1",
"@rollup/plugin-typescript": "^8.3.0",
"@sentry/browser": "^6.16.1",
"@sentry/tracing": "^6.16.1",
"@sentry/browser": "^6.17.3",
"@sentry/tracing": "^6.17.3",
"@squoosh/cli": "^0.7.2",
"@trivago/prettier-plugin-sort-imports": "^3.1.1",
"@types/chart.js": "^2.9.35",
"@types/codemirror": "5.60.5",
"@types/grecaptcha": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"@webcomponents/webcomponentsjs": "^2.6.0",
"babel-plugin-macros": "^3.1.0",
"base64-js": "^1.5.1",
"chart.js": "^3.7.0",
"chartjs-adapter-moment": "^1.0.0",
"codemirror": "^5.65.1",
"construct-style-sheets-polyfill": "^3.0.5",
"country-flag-icons": "^1.4.19",
"eslint": "^8.7.0",
"construct-style-sheets-polyfill": "^3.1.0",
"country-flag-icons": "^1.4.20",
"eslint": "^8.8.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.4",
"eslint-plugin-lit": "^1.6.1",
"flowchart.js": "^1.17.0",
"fuse.js": "^6.5.3",
"lit": "^2.1.1",
"lit": "^2.1.2",
"moment": "^2.29.1",
"prettier": "^2.5.1",
"rapidoc": "^9.1.3",
"rollup": "^2.64.0",
"rapidoc": "^9.1.4",
"rollup": "^2.66.1",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",
"rollup-plugin-minify-html-literals": "^1.2.6",

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2022.1.1";
export const VERSION = "2022.1.5";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -113,6 +113,9 @@ export class FlowExecutor extends LitElement implements StageHost {
.pf-c-drawer__content {
background-color: transparent;
}
.pf-c-login__main {
width: 100%;
}
`);
}

View File

@ -1,11 +1,13 @@
import { en, fr, tr } from "make-plural/plurals";
import { en, es, fr, pl, tr } from "make-plural/plurals";
import { Messages, i18n } from "@lingui/core";
import { detect, fromNavigator, fromStorage, fromUrl } from "@lingui/detect-locale";
import { t } from "@lingui/macro";
import { messages as localeEN } from "../locales/en";
import { messages as localeES } from "../locales/es";
import { messages as localeFR_FR } from "../locales/fr_FR";
import { messages as localePL } from "../locales/pl";
import { messages as localeDEBUG } from "../locales/pseudo-LOCALE";
import { messages as localeTR } from "../locales/tr";
@ -29,7 +31,7 @@ export const LOCALES: {
locale: localeDEBUG,
},
{
code: "fr_FR",
code: "fr",
plurals: fr,
label: t`French`,
locale: localeFR_FR,
@ -40,6 +42,18 @@ export const LOCALES: {
label: t`Turkish`,
locale: localeTR,
},
{
code: "es",
plurals: es,
label: t`Spanish`,
locale: localeES,
},
{
code: "pl",
plurals: pl,
label: t`Polish`,
locale: localePL,
},
];
LOCALES.forEach((locale) => {
@ -50,9 +64,13 @@ LOCALES.forEach((locale) => {
const DEFAULT_FALLBACK = () => "en";
export function autoDetectLanguage() {
const detected =
let detected =
detect(fromUrl("lang"), fromStorage("lang"), fromNavigator(), DEFAULT_FALLBACK) ||
DEFAULT_FALLBACK();
// For now we only care about the first locale part
if (detected.includes("_")) {
detected = detected.split("_")[0];
}
if (detected in i18n._messages) {
console.debug(`authentik/locale: Activating detected locale '${detected}'`);
i18n.activate(detected);

View File

@ -3608,6 +3608,10 @@ msgstr "Policy {0}"
msgid "Policy-specific settings"
msgstr "Policy-specific settings"
#: src/interfaces/locale.ts
msgid "Polish"
msgstr "Polish"
#: src/pages/providers/saml/SAMLProviderForm.ts
msgid "Post"
msgstr "Post"
@ -4433,6 +4437,10 @@ msgstr "Sources"
msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
#: src/interfaces/locale.ts
msgid "Spanish"
msgstr "Spanish"
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "Specify multiple server URIs by separating them with a comma."
msgstr "Specify multiple server URIs by separating them with a comma."

6090
web/src/locales/es.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3580,6 +3580,10 @@ msgstr "Poliitique {0}"
msgid "Policy-specific settings"
msgstr "Paramètres spécifiques à la politique"
#: src/interfaces/locale.ts
msgid "Polish"
msgstr ""
#: src/pages/providers/saml/SAMLProviderForm.ts
msgid "Post"
msgstr "Appliquer"
@ -4395,6 +4399,10 @@ msgstr "Sources"
msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "Sources d'identités, qui peuvent soit être synchronisées dans la base de données d'Authentik, soit être utilisées par les utilisateurs pour s'authentifier et s'inscrire."
#: src/interfaces/locale.ts
msgid "Spanish"
msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "Specify multiple server URIs by separating them with a comma."
msgstr ""

6090
web/src/locales/pl.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3598,6 +3598,10 @@ msgstr ""
msgid "Policy-specific settings"
msgstr ""
#: src/interfaces/locale.ts
msgid "Polish"
msgstr ""
#: src/pages/providers/saml/SAMLProviderForm.ts
msgid "Post"
msgstr ""
@ -4423,6 +4427,10 @@ msgstr ""
msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr ""
#: src/interfaces/locale.ts
msgid "Spanish"
msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "Specify multiple server URIs by separating them with a comma."
msgstr ""

View File

@ -3546,6 +3546,10 @@ msgstr "İlke {0}"
msgid "Policy-specific settings"
msgstr "İlke özel ayarlar"
#: src/interfaces/locale.ts
msgid "Polish"
msgstr ""
#: src/pages/providers/saml/SAMLProviderForm.ts
msgid "Post"
msgstr "Post"
@ -4344,6 +4348,10 @@ msgstr "Kaynaklar"
msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "Auentik'in veritabanına senkronize edilebilen ya da kullanıcılar tarafından kimlik doğrulaması ve kayıt yaptırmak için kullanılabilen kimliklerin kaynakları."
#: src/interfaces/locale.ts
msgid "Spanish"
msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "Specify multiple server URIs by separating them with a comma."
msgstr "Birden çok sunucu URI'lerini virgülle ayırarak belirtin."

View File

@ -110,7 +110,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="pf-icon pf-icon-server"
icon="pf-icon pf-icon-process-automation"
header=${t`Flows`}
headerLink="#/flow/flows"
>
@ -121,7 +121,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-zone"
header=${t`Outpost status`}
headerLink="#/outpost/outposts"
>
@ -132,7 +132,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-user"
header=${t`Users`}
headerLink="#/identity/users"
>
@ -143,7 +143,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-users"
header=${t`Groups`}
headerLink="#/identity/groups"
>

View File

@ -216,6 +216,7 @@ export class SAMLProviderViewPage extends LitElement {
${t`Download`}
</a>
<ak-action-button
class="pf-m-secondary"
.apiRequest=${() => {
const fullUrl =
window.location.origin +

View File

@ -34,7 +34,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
}
@property({ attribute: false })
provider?: ProviderEnum;
provider: ProviderEnum = ProviderEnum.Twilio;
@property({ attribute: false })
authType?: AuthTypeEnum;
@ -60,6 +60,101 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
}
};
renderProviderTwillio(): TemplateResult {
return html` <ak-form-element-horizontal
label=${t`Twilio Account SID`}
?required=${true}
name="accountSid"
>
<input
type="text"
value="${ifDefined(this.instance?.accountSid || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`Get this value from https://console.twilio.com`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Twilio Auth Token`} ?required=${true} name="auth">
<input
type="text"
value="${ifDefined(this.instance?.auth || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`Get this value from https://console.twilio.com`}
</p>
</ak-form-element-horizontal>`;
}
renderProviderGeneric(): TemplateResult {
return html` <ak-form-element-horizontal
label=${t`Authentication Type`}
@change=${(ev: Event) => {
const current = (ev.target as HTMLInputElement).value;
this.authType = current as AuthTypeEnum;
}}
?required=${true}
name="authType"
>
<select class="pf-c-form-control">
<option
value="${AuthTypeEnum.Basic}"
?selected=${this.instance?.authType === AuthTypeEnum.Basic}
>
${t`Basic Auth`}
</option>
<option
value="${AuthTypeEnum.Bearer}"
?selected=${this.instance?.authType === AuthTypeEnum.Bearer}
>
${t`Bearer Token`}
</option>
</select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`External API URL`}
?required=${true}
name="accountSid"
>
<input
type="text"
value="${ifDefined(this.instance?.accountSid || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`This is the full endpoint to send POST requests to.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`API Auth Username`} ?required=${true} name="auth">
<input
type="text"
value="${ifDefined(this.instance?.auth || "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`This is the username to be used with basic auth or the token when used with bearer token`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`API Auth password`}
?required=${false}
name="authPassword"
>
<input
type="text"
value="${ifDefined(this.instance?.authPassword)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`This is the password to be used with basic auth`}
</p>
</ak-form-element-horizontal>`;
}
renderForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal">
<div class="form-help-text">
@ -117,113 +212,9 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
${t`Number the SMS will be sent from.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Twilio Account SID`}
?hidden=${this.provider !== ProviderEnum.Twilio}
?required=${true}
name="accountSid"
>
<input
type="text"
value="${ifDefined(this.instance?.accountSid || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`Get this value from https://console.twilio.com`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Twilio Auth Token`}
?hidden=${this.provider !== ProviderEnum.Twilio}
?required=${true}
name="auth"
>
<input
type="text"
value="${ifDefined(this.instance?.auth || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`Get this value from https://console.twilio.com`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Authentication Type`}
?hidden=${this.provider !== ProviderEnum.Generic}
@change=${(ev: Event) => {
const current = (ev.target as HTMLInputElement).value;
this.authType = current as AuthTypeEnum;
}}
?required=${true}
name="authType"
>
<select class="pf-c-form-control">
<option
value="${AuthTypeEnum.Basic}"
?selected=${this.instance?.authType === AuthTypeEnum.Basic}
>
${t`Basic Auth`}
</option>
<option
value="${AuthTypeEnum.Bearer}"
?selected=${this.instance?.authType === AuthTypeEnum.Bearer}
>
${t`Bearer Token`}
</option>
</select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`External API URL`}
?hidden=${this.provider !== ProviderEnum.Generic}
?required=${true}
name="accountSid"
>
<input
type="text"
value="${ifDefined(this.instance?.accountSid || "")}"
class="pf-c-form-control"
required
/>
<p class="pf-c-form__helper-text">
${t`This is the full endpoint to send POST requests to.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`API Auth Username`}
?hidden=${this.provider !== ProviderEnum.Generic}
?required=${true}
name="auth"
>
<input
type="text"
value="${ifDefined(this.instance?.auth || "")}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`This is the username to be used with basic auth or the token when used with bearer token`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`API Auth password`}
?hidden=${!(
this.provider === ProviderEnum.Generic &&
this.authType === AuthTypeEnum.Basic
)}
?required=${false}
name="authPassword"
>
<input
type="text"
value="${ifDefined(this.instance?.authPassword)}"
class="pf-c-form-control"
/>
<p class="pf-c-form__helper-text">
${t`This is the password to be used with basic auth`}
</p>
</ak-form-element-horizontal>
${this.provider === ProviderEnum.Generic
? this.renderProviderGeneric()
: this.renderProviderTwillio()}
<ak-form-element-horizontal label=${t`Configuration flow`} name="configureFlow">
<select class="pf-c-form-control">
<option

View File

@ -227,6 +227,7 @@ export class UserListPage extends TablePage<User> {
}
return html`
<ak-action-button
class="pf-m-secondary"
.apiRequest=${() => {
return new CoreApi(DEFAULT_CONFIG)
.coreUsersRecoveryRetrieve({

View File

@ -103,7 +103,7 @@ export class UserDetailsForm extends ModelForm<UserSelf, number> {
value=${locale.code}
?selected=${config.locale === locale.code}
>
${locale.label}
${locale.code.toUpperCase()} - ${locale.label}
</option>`;
})}
</select>

View File

@ -0,0 +1,38 @@
---
title: Writing documentation
---
Writing documentation for authentik is a great way for both new and experienced users to improve and contribute to the project. Here are a few guidelines to ensure
the documentation is easy to read and uses similar phrasing.
# General guidelines
- authentik should always be stylized as `authentik` (with a lower-case a and ending with a k)
- Documentation should use American english
- Feel free to use Docusaurus-specific features, see [here](https://docusaurus.io/docs/next/markdown-features)
- Use abbreviations where it makes sense (for commonly used terms like SAML and OAuth)
- Phrasing should never blame the user, and should be subjective, i.e
- **DON'T** `You may never click x.`
- **DO** `x should never be clicked.`
- When referring to other objects in authentik, use cursive text, and link to the corresponding documentation if possible.
- When referring to external tools, give an example how to use the tools or explain how the user can use them.
- Make sure to add the documentation to add to the sidebar, if adding a new page.
- Test how the documentation renders using the Netlify Preview, especially when using Docusaurus-specific features.
If you find any documentation that doesn't match these guidelines, feel free to either open an Issue or a PR so they can be fixed.
## Integration guidelines
These guidelines apply in addition to the ones above.
- For placeholders, use angle brackets (`<placeholder-name>`).
Make sure to also define if the placeholder is something the user needs to define, something another system defines, or randomly generated.
If you're adding configuration snippets to the documentation, and the snippet is in a language that supports comments,
other placeholders may be used, for example comments referencing an earlier step.
- For placeholder domains, use `authentik.company` and `app-name.company`, where `app-name` is the name of the application you are writing documentation for.
- Try to order the documentation in the order that makes it easiest for the user to configure.

View File

@ -1,11 +1,15 @@
---
title: Translation
title: Translations
---
Translation in authentik is done in two places. Most of the text is defined in the frontend in `web/`, and a subset of messages is defined in the backend.
The frontend uses [lingui](https://lingui.js.org/), and the backend uses the built-in django translation tools.
:::info
Please review the [Writing documentation](./docs/writing-documentation) guidelines as they apply to documentation too.
:::
## Online translation
To simplify translation you can use https://www.transifex.com/beryjuorg/authentik/, which has no local requirements.

View File

@ -24,6 +24,9 @@ The following aspects can be configured:
- *Name*: This is the name shown for the application card
- *Launch URL*: The URL that is opened when a user clicks on the application. When left empty, authentik tries to guess it based on the provider
Starting with authentik 2022.2, you can use placeholders in the launch url to build them dynamically based on logged in user. For example, you can set the Launch URL to `https://goauthentik.io/%(username)s`, which will be replaced with the currently logged in user's username.
- *Icon (URL)*: Optionally configure an Icon for the application
- *Publisher*: Text shown below the application
- *Description*: Subtext shown on the application card below the publisher

View File

@ -18,6 +18,17 @@ Example:
user_email_local = regex_replace(request.user.email, '(.+)@.+', '')
```
### `list_flatten(value: list[Any] | Any) -> Optional[Any}`
Flatten a list by either returning its first element, None if the list is empty, or the passed in object if its not a list.
Example:
```python
user = list_flatten(["foo"])
# user = "foo"
```
### `ak_is_group_member(user: User, **group_filters) -> bool`
Check if `user` is member of a group matching `**group_filters`.

View File

@ -25,7 +25,11 @@ All of these variables can be set to values, but you can also use a URI-like for
### PostgreSQL Backup Settings
- `AUTHENTIK_POSTGRESQL__BACKUP__ENABLED`: Controls if the inbuilt backup-mechanism is enabled, defaults to true (new in 2021.10).
:::info
The integrated backup is deprecated in 2022.1 and will be removed in a future version.
:::
- `AUTHENTIK_POSTGRESQL__BACKUP__ENABLED`: Controls if the inbuilt backup-mechanism is enabled, defaults to false (new in 2021.10).
Optionally enable automated database backups to S3 or S3-compatible storages.

View File

@ -2,6 +2,10 @@
title: Backup and restore
---
:::error
Integrated backups are deprecated and will be removed in a future authentik release.
:::
:::warning
Local backups are only supported for docker-compose installs. If you want to backup a Kubernetes instance locally, use an S3-compatible server such as [minio](https://min.io/)
:::

View File

@ -50,6 +50,9 @@ docker_network: null
# Optionally disable mapping of ports to outpost container, may be useful when using docker networks
# (Available with 2021.9.4+)
docker_map_ports: true
# Optionally additional labels for docker containers
# (Available with 2022.1.2)
docker_labels: null
########################################
# Kubernetes outpost specific settings
########################################

View File

@ -12,6 +12,7 @@ The following outpost settings are used:
- `container_image`: Optionally overwrites the standard container image (see [Configuration](../../installation/configuration.md) to configure the global default)
- `docker_network`: The docker network the container should be added to. This needs to be modified if you plan to connect to authentik using the internal hostname.
- `docker_map_ports`: Enable/disable the mapping of ports. When using a proxy outpost with traefik for example, you might not want to bind ports as they are routed through traefik.
- `docker_labels`: Optional additional labels that can be applied to the container.
The container is created with the following hardcoded properties:
@ -22,12 +23,12 @@ The container is created with the following hardcoded properties:
Additionally, the proxy outposts have the following extra labels to add themselves into traefik automatically.
- `traefik.enable`: "true"
- `traefik.http.routers.ak-outpost-<outpost-id>-router.rule`: `Host(...)`
- `traefik.http.routers.ak-outpost-<outpost-id>-router.service`: `ak-outpost-<outpost-id>-service`
- `traefik.http.routers.ak-outpost-<outpost-id>-router.tls`: "true"
- `traefik.http.services.ak-outpost-<outpost-id>-service.loadbalancer.healthcheck.path`: "/akprox/ping"
- `traefik.http.services.ak-outpost-<outpost-id>-service.loadbalancer.healthcheck.port`: "9300"
- `traefik.http.services.ak-outpost-<outpost-id>-service.loadbalancer.server.port`: "9000"
- `traefik.http.routers.ak-outpost-<outpost-name>-router.rule`: `Host(...)`
- `traefik.http.routers.ak-outpost-<outpost-name>-router.service`: `ak-outpost-<outpost-name>-service`
- `traefik.http.routers.ak-outpost-<outpost-name>-router.tls`: "true"
- `traefik.http.services.ak-outpost-<outpost-name>-service.loadbalancer.healthcheck.path`: "/akprox/ping"
- `traefik.http.services.ak-outpost-<outpost-name>-service.loadbalancer.healthcheck.port`: "9300"
- `traefik.http.services.ak-outpost-<outpost-name>-service.loadbalancer.server.port`: "9000"
## Permissions

View File

@ -32,5 +32,5 @@ metadata:
nginx.ingress.kubernetes.io/auth-response-headers: |
Set-Cookie,X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header Host $http_host;
```

View File

@ -6,8 +6,6 @@ For Nginx Proxy Manager you can use this snippet
# header from upstream' error when trying to access an application protected by goauthentik
proxy_buffers 8 16k;
proxy_buffer_size 32k;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
location / {
# Put your proxy_pass to your application here
@ -16,6 +14,8 @@ location / {
# authentik-specific config
auth_request /akprox/auth/nginx;
error_page 401 = @akprox_signin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# translate headers from the outposts back to the actual upstream
auth_request_set $authentik_username $upstream_http_x_authentik_username;
@ -37,6 +37,7 @@ location /akprox {
# ensure the host of this vserver matches your external URL you've configured
# in authentik
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie $upstream_http_set_cookie;
}

View File

@ -13,8 +13,6 @@ server {
# header from upstream' error when trying to access an application protected by goauthentik
proxy_buffers 8 16k;
proxy_buffer_size 32k;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
location / {
# Put your proxy_pass to your application here
@ -25,6 +23,8 @@ server {
error_page 401 = @akprox_signin;
# For domain level, use the below error_page to redirect to your authentik server with the full redirect path
# error_page 401 =302 https://authentik.company/akprox/start?rd=$scheme://$http_host$request_uri;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# translate headers from the outposts back to the actual upstream
auth_request_set $authentik_username $upstream_http_x_authentik_username;
@ -46,6 +46,7 @@ server {
# ensure the host of this vserver matches your external URL you've configured
# in authentik
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie $upstream_http_set_cookie;
}

View File

@ -27,6 +27,14 @@ This release mostly removes legacy fields and features that have been deprecated
The deprecated /api/v2beta/ Endpoint is removed. Use `/api/v3/`.
- Backup:
The integrated backup has been deprecated for the following reasons:
- Difficulty with restores not working properly
- Inflexible configuration (fixed retention, limited to once a day, only S3 supported)
- Most users will already have an existing backup infrastructure
## Minor changes/fixes
- core: dont return 404 when trying to view key of expired token
@ -50,6 +58,72 @@ This release mostly removes legacy fields and features that have been deprecated
- web/admin: fix missing configure flow setting on webuahtn setup stage form
- web/flows: remove node directly instead of using removeChild()
## Fixed in 2022.1.2
- internal/proxyv2: only allow access to /akprox in nginx mode when forward url could be extracted
- lib: disable backup by default, add note to configuration
- lifecycle: replace lowercase, deprecated prometheus_multiproc_dir
- outposts: allow custom label for docker containers
- policies/hibp: ensure password is encodable
- providers/proxy: add PathPrefix to auto-traefik labels
- root: upgrade python dependencies
## Fixed in 2022.1.3
- internal: add support for X-Original-URL
- internal: add optional debug server listening on 9900
- internal: don't override server header
- internal: start adding tests to outpost
- lifecycle: make secret_key warning more prominent
- lifecycle: wait for db in worker
- outposts/ldap: Fix more case sensitivity issues. (#2144)
- outposts/proxy: add more test cases for domain-level auth
- outposts/proxy: fix potential empty redirect, add tests
- outposts/proxy: trace full headers to debug
- providers/proxy: fix traefik label
- root: add max-requests for gunicorn and max tasks for celery
- root: fix redis passwords not being encoded correctly
- web/admin: fix links which look like labels
- web/admin: fix SMS Stage form not working
## Fixed in 2022.1.4
- core: fix view_token permission not being assigned on token creation for non-admin user
- lifecycle: remove gunicorn reload option
- lifecycle: send analytics in gunicorn config to decrease outgoing requests when workers get restarted
- providers/proxy: add support for X-Original-URI in nginx, better handle missing headers and report errors to authentik
- providers/proxy: don't include hostname and scheme in redirect when we only got a path and not a full URL
- providers/proxy: fix routing for external_host when using forward_auth_domain
- providers/proxy: set traefik labels using object_naming_template instead of UUID
- sources/ldap: add list_flatten function to property mappings, enable on managed LDAP mappings
- web: add es locale
- web: add pl locale
- web/admin: only check first half of locale when detecting
- web/flows: fix width on flow container
- web/user: include locale code in locale selection
## Fixed in 2022.1.5
- build(deps): bump uvicorn from 0.17.1 to 0.17.3 (#2229)
- core: allow formatting strings to be used for applications' launch URLs
- internal: don't attempt to lookup SNI Certificate if no SNI is sent
- internal: fix CSRF error caused by Host header
- internal: improve error handling for internal reverse proxy
- internal: remove uvicorn server header
- internal: trace headers and url for backend requests
- outposts: fix channel not always having a logger attribute
- outposts: fix compare_ports to support both service and container ports
- outposts: fix service reconciler re-creating services
- outposts: remove node_port on V1ServicePort checks to prevent service creation loops
- providers/proxy: fix Host/:Authority not being modified
- providers/proxy: fix nil error in claims
- providers/proxy: improve error handling for invalid backend_override
- sources/ldap: log entire exception
- sources/saml: fix incorrect ProtocolBinding being sent
- sources/saml: fix server error
- stages/authenticator_validate: handle non-existent device_challenges
- web/admin: fix mismatched icons in overview and lists
## Upgrading
This release does not introduce any new requirements.

View File

@ -0,0 +1,72 @@
---
title: Zulip
---
## What is Zulip
From https://zulip.com
:::note
**Zulip**: Chat for distributed teams. Zulip combines the immediacy of real-time chat with an email threading model.
With Zulip, you can catch up on important conversations while ignoring irrelevant ones.
:::
## Preperation
The following placeholders will be used:
- `authentik.company` is the FQDN of the authentik install.
- `zulip.company` is the FQDN of the Zulip instance.
Create an application in authentik and note the slug, as this will be used later. Create a SAML provider with the following parameters:
- ACS URL: `https://zulip.company/complete/saml/`
- Issuer: `https://authentik.company`
- Service Provider Binding: `Post`
- Signing Keypair: Select any certificate you have.
- Property mappings: Select all Managed mappings.
## Zulip Configuration
Zulip is a Django application and is configured using `/etc/zulip/settings.py`. Only settings that differ
from the defaults are displayed below. Please make sure you have the latest `settings.py` file as more settings
might have been added to defaults since you installed Zulip.
Uncomment `zproject.backends.SAMLAuthBackend` inside the `AUTHENTICATION_BACKENDS` parameter to enable SAML support
and fill in the following required configuration.
```
SOCIAL_AUTH_SAML_ORG_INFO = {
"en-US": {
"displayname": "authentik Zulip",
"name": "zulip",
"url": "{}{}".format("https://", EXTERNAL_HOST),
},
}
SOCIAL_AUTH_SAML_ENABLED_IDPS: Dict[str, Any] = {
# idp identifier and settings
"authentik": {
# KEEP OTHER SETTINGS AS DEFAULT OR CONFIGURE THEM ACCORDING TO YOUR PREFERENCES
"entity_id": "https://authentik.company",
"url": "https://authentik.company/application/saml/<application slug>/sso/binding/redirect/",
"display_name": "authentik SAML",
},
}
```
Place the certificate you associated with the SAML provider in authentik inside the `/etc/zulip/saml/idps` directory.
The certificate file name must match the idp identifier name you set in the configuration (i.e. authentik.crt).
:::note
Remember to restart Zulip.
:::
## Additional Resources
Please refer to the following for further information:
- https://zulip.com/
- https://zulip.readthedocs.io
- https://chat.zulip.org/ (Official public Zulip Chat instance)

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