Compare commits

..

64 Commits

Author SHA1 Message Date
941bc61b31 release: 2021.9.3 2021-09-27 17:31:50 +02:00
282b364606 stages/prompt: fix inconsistent policy context for validation policies
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-27 17:05:26 +02:00
ad4bc4083d website/docs: update dev docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-27 16:04:41 +02:00
ebe282eb1a web/admin: fix user_write form not writing group
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-27 10:12:45 +02:00
830c26ca25 tests/e2e: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-27 09:52:47 +02:00
ed3b4a3d4a build(deps): bump rapidoc from 9.1.2 to 9.1.3 in /website (#1478)
Bumps [rapidoc](https://github.com/mrin9/RapiDoc) from 9.1.2 to 9.1.3.
- [Release notes](https://github.com/mrin9/RapiDoc/releases)
- [Commits](https://github.com/mrin9/RapiDoc/compare/v9.1.2...v9.1.3)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:24:43 +02:00
975c4ddc04 build(deps): bump postcss from 8.3.7 to 8.3.8 in /website (#1479)
Bumps [postcss](https://github.com/postcss/postcss) from 8.3.7 to 8.3.8.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.3.7...8.3.8)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:22:49 +02:00
7e2896298a build(deps): bump rapidoc from 9.1.2 to 9.1.3 in /web (#1480)
Bumps [rapidoc](https://github.com/mrin9/RapiDoc) from 9.1.2 to 9.1.3.
- [Release notes](https://github.com/mrin9/RapiDoc/releases)
- [Commits](https://github.com/mrin9/RapiDoc/compare/v9.1.2...v9.1.3)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:22:31 +02:00
cba9cf8361 build(deps): bump actions/github-script from 4.1 to 5 (#1481)
Bumps [actions/github-script](https://github.com/actions/github-script) from 4.1 to 5.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v4.1...v5)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:22:21 +02:00
bf12580f64 build(deps): bump pycryptodome from 3.10.3 to 3.10.4 (#1482)
Bumps [pycryptodome](https://github.com/Legrandin/pycryptodome) from 3.10.3 to 3.10.4.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/commits/v3.10.4)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-27 09:22:03 +02:00
75ef4ce596 tests/e2e: add new ldap object classes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:57:42 +02:00
c2f3ce11b0 outposts/ldap: fix potential panic when converting attributes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:52:25 +02:00
3c256fecc6 outposts/ldap: add groupofuniquenames
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:49:11 +02:00
0285b84133 outposts/ldap: add query support for all supported object classes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:42:26 +02:00
99a371a02c web/elements: fix token copy button not working on chrome...
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:34:28 +02:00
c7e6eb8896 outposts/ldap: add support for base scope and domain info
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 14:01:22 +02:00
674bd9e05c web/admin: Fix typo 'username address' -> 'username' (#1473) 2021-09-26 12:53:37 +02:00
b79901df87 website/docs: prepare 2021.9.3
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 12:03:10 +02:00
b248f450dd outposts: make AUTHENTIK_HOST_BROWSER configurable from central config
closes #1471

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 12:00:51 +02:00
05db9e5c40 web/admin: handle error correctly when creating user recovery link
closes #1472

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 11:49:40 +02:00
234a5e2b66 outposts: fix outposts not correctly updating central state
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-26 11:40:21 +02:00
aea1736f70 outposts/proxy: Fix failing traefik healtcheck (#1470) 2021-09-26 11:33:18 +02:00
9f4a4449f5 outposts/proxy: ensure cookies only last as long as tokens
closes #1462

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-25 16:12:59 +02:00
b6b55e2336 build(deps): bump goauthentik.io/api from 0.202192.3 to 0.202192.5 (#1468)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.202192.3 to 0.202192.5.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.202192.3...v0.202192.5)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-25 16:06:10 +02:00
8f2805e05b web: Update Web API Client version (#1467)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-25 16:04:07 +02:00
4f3583cd7e providers/proxy: make token_validity float and optional for backwards compat
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-25 15:54:32 +02:00
617e90dca3 web: Update Web API Client version (#1465)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-25 15:48:05 +02:00
f7408626a8 providers/proxy: return token_validity as total seconds instead of expression
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-25 15:44:16 +02:00
4dcb15af46 build(deps): bump goauthentik.io/api from 0.202192.1 to 0.202192.3 (#1464)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.202192.1 to 0.202192.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.202192.1...v0.202192.3)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-25 15:11:08 +02:00
89beb7a9f7 web: Update Web API Client version (#1463)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-25 15:02:33 +02:00
28eeb4798e providers/proxy: add token_validity field for outpost configuration
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#1462
2021-09-25 15:00:06 +02:00
79b92e764e *: fix typos in code
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-25 00:01:11 +02:00
919336a519 outposts: ensure service is always re-created with mismatching ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 23:45:15 +02:00
27e04589c1 outposts/proxyv2: fix routing not working correctly for domain auth
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 23:32:16 +02:00
ba44fbdac8 website/docs: fix typos and grammar (#1459) 2021-09-24 15:37:54 +02:00
0e093a8917 web: Update Web API Client version (#1458) 2021-09-24 12:23:14 +02:00
d0bfb99859 web/elements: improve error handling on forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 12:19:56 +02:00
93bdea3769 core: fix api return code for user self-update
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 11:51:03 +02:00
e681654af7 web/admin: add notice for recovery
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 11:50:52 +02:00
cab7593dca web/user: fix brand not being shown in safari
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 11:50:46 +02:00
cf92f9aefc web/elements: fix token copy error in safari
closes #1219

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 10:44:28 +02:00
8d72b3498d internal: fix typo
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 10:44:28 +02:00
42ab858c50 build(deps): bump goauthentik.io/api from 0.202191.4 to 0.202192.1 (#1455)
Bumps [goauthentik.io/api](https://github.com/goauthentik/client-go) from 0.202191.4 to 0.202192.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v0.202191.4...v0.202192.1)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-24 09:34:10 +02:00
a1abae9ab1 build(deps): bump boto3 from 1.18.46 to 1.18.47 (#1456)
Bumps [boto3](https://github.com/boto/boto3) from 1.18.46 to 1.18.47.
- [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.18.46...1.18.47)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-24 09:34:01 +02:00
8f36b49061 web/user: search apps when user typed before apps have loaded
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 16:34:11 +02:00
64b4e851ce events: add additional validation for event transport
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 16:29:58 +02:00
40a62ac1e5 web/admin: fix Transport Form not loading mode correctly on edit
closes #1453

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 16:16:38 +02:00
5df60e4d87 web/admin: fix NotificationWebhookMapping not loading correctly
closes #1452

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 16:13:58 +02:00
50ebc8522d web: Update Web API Client version (#1454)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-23 14:21:49 +02:00
eddca478dc release: 2021.9.2 2021-09-23 12:34:02 +02:00
99a7fca08e website/docs: prepare 2021.9.2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 12:33:42 +02:00
a7e3602908 web: fix import order of polyfills causing shadydom to not work on firefox and safari
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 10:16:49 +02:00
74169860cf api: add logging to sentry proxy
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 09:57:42 +02:00
52bb774f73 internal: add asset paths for user interface
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-23 09:57:24 +02:00
f26fcaf825 website/docs: add warning for example flows (#1444) 2021-09-23 08:34:40 +02:00
b8e92e2f11 build(deps): bump postcss from 8.3.6 to 8.3.7 in /website (#1445) 2021-09-23 08:33:27 +02:00
08adfc94d6 build(deps): bump rollup from 2.56.3 to 2.57.0 in /web (#1446) 2021-09-23 08:33:18 +02:00
236fafb735 build(deps): bump boto3 from 1.18.45 to 1.18.46 (#1447) 2021-09-23 08:33:10 +02:00
5ad9ddee3c build(deps): bump goauthentik.io/api from 0.202191.3 to 0.202191.4 (#1449) 2021-09-23 08:33:01 +02:00
24d220ff49 build(deps): bump urllib3 from 1.26.6 to 1.26.7 (#1448) 2021-09-23 08:32:53 +02:00
3364c195b7 build(deps): bump sentry-sdk from 1.4.0 to 1.4.1 (#1450) 2021-09-23 08:32:43 +02:00
50aa87d141 web/user: enable sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-22 22:35:52 +02:00
72b375023d web: Update Web API Client version (#1443)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-22 20:53:25 +02:00
77ba186818 website/docs: add notice for guacamole token length
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-22 20:02:30 +02:00
113 changed files with 760 additions and 421 deletions

View File

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

1
.github/codespell-words.txt vendored Normal file
View File

@ -0,0 +1 @@
keypair

View File

@ -33,14 +33,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2021.9.1,
beryju/authentik:2021.9.3,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.9.1,
ghcr.io/goauthentik/server:2021.9.3,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.9.1', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2021.9.3', 'rc') }}
run: |
docker pull beryju/authentik:latest
docker tag beryju/authentik:latest beryju/authentik:stable
@ -75,14 +75,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-proxy:2021.9.1,
beryju/authentik-proxy:2021.9.3,
beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.9.1,
ghcr.io/goauthentik/proxy:2021.9.3,
ghcr.io/goauthentik/proxy:latest
file: proxy.Dockerfile
platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.9.1', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2021.9.3', 'rc') }}
run: |
docker pull beryju/authentik-proxy:latest
docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable
@ -117,14 +117,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-ldap:2021.9.1,
beryju/authentik-ldap:2021.9.3,
beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.9.1,
ghcr.io/goauthentik/ldap:2021.9.3,
ghcr.io/goauthentik/ldap:latest
file: ldap.Dockerfile
platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.9.1', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2021.9.3', 'rc') }}
run: |
docker pull beryju/authentik-ldap:latest
docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable
@ -175,7 +175,7 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2021.9.1
version: authentik@2021.9.3
environment: beryjuorg-prod
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -27,7 +27,7 @@ jobs:
docker-compose run -u root server test
- name: Extract version number
id: get_version
uses: actions/github-script@v4.1
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@ -20,6 +20,11 @@ test:
lint-fix:
isort authentik tests lifecycle
black authentik tests lifecycle
codespell -I .github/codespell-words.txt -w authentik
codespell -I .github/codespell-words.txt -w internal
codespell -I .github/codespell-words.txt -w cmd
codespell -I .github/codespell-words.txt -w web/src
codespell -I .github/codespell-words.txt -w website/src
lint:
pyright authentik tests lifecycle

View File

@ -48,6 +48,7 @@ duo-client = "*"
ua-parser = "*"
deepmerge = "*"
colorama = "*"
codespell = "*"
[dev-packages]
bandit = "*"

210
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "19d5324fd1a4af125ed57a683030ca14ee2d3648117748e4b32656875484728e"
"sha256": "babb6061c555f8f239f00210b2a0356763bdaaca2f3d704cf3444891b84db84d"
},
"pipfile-spec": 6,
"requires": {},
@ -120,19 +120,19 @@
},
"boto3": {
"hashes": [
"sha256:68ee81a7ef40380a5ab973e242bbf8739d56a49f8691508c48760fb5066933e3",
"sha256:c2fd29e53464e4ab79c224363c20a02af19f7ecc8baf37f7886a893fc672272a"
"sha256:7b45b224442c479de4bc6e6e9cb0557b642fc7a77edc8702e393ccaa2e0aa128",
"sha256:c388da7dc1a596755f39de990a72e05cee558d098e81de63de55bd9598cc5134"
],
"index": "pypi",
"version": "==1.18.45"
"version": "==1.18.48"
},
"botocore": {
"hashes": [
"sha256:1d31e461dfc9ddb9a86fdd47ebc61751adc8739b4f7160c687c04092e5fbe0aa",
"sha256:b3a77dcc7d54a3725aa0a9b44e4a79142a013584cd0568750a9ee9ab6970538f"
"sha256:2c25a76f09223b2f00ad578df34492b7b84cd4828fc90c08ccbdd1d70abbd7eb",
"sha256:9d5b70be2f417d0aa30788049fd20473ad27218eccd05e71f545b4b4e09c79a0"
],
"markers": "python_version >= '3.6'",
"version": "==1.21.45"
"version": "==1.21.48"
},
"cachetools": {
"hashes": [
@ -286,6 +286,14 @@
],
"version": "==0.2.0"
},
"codespell": {
"hashes": [
"sha256:19d3fe5644fef3425777e66f225a8c82d39059dcfe9edb3349a8a2cf48383ee5",
"sha256:b864c7d917316316ac24272ee992d7937c3519be4569209c5b60035ac5d569b5"
],
"index": "pypi",
"version": "==2.1.0"
},
"colorama": {
"hashes": [
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
@ -371,11 +379,11 @@
},
"django-filter": {
"hashes": [
"sha256:84e9d5bb93f237e451db814ed422a3a625751cbc9968b484ecc74964a8696b06",
"sha256:e00d32cebdb3d54273c48f4f878f898dced8d5dfaad009438fe61ebdf535ace1"
"sha256:632a251fa8f1aadb4b8cceff932bb52fe2f826dd7dfe7f3eac40e5c463d6836e",
"sha256:f4a6737a30104c98d2e2a5fb93043f36dd7978e0c7ddc92f5998e85433ea5063"
],
"index": "pypi",
"version": "==2.4.0"
"version": "==21.1"
},
"django-guardian": {
"hashes": [
@ -482,11 +490,11 @@
},
"geoip2": {
"hashes": [
"sha256:599914784cea08b50fb50c22ed6a59143b5ff2d027ba782d2d5b6f3668293821",
"sha256:7ad10942e9fd6c54bc850d8311618f04dacd3fff5b55ba48b7ad83502fc884bd"
"sha256:f150bed3190d543712a17467208388d31bd8ddb49b2226fba53db8aaedb8ba89",
"sha256:f9172cdfb2a5f9225ace5e30dd7426413ad28798a5f474cd1538780686bd6a87"
],
"index": "pypi",
"version": "==4.3.0"
"version": "==4.4.0"
},
"google-auth": {
"hashes": [
@ -706,10 +714,10 @@
},
"maxminddb": {
"hashes": [
"sha256:c47b8acba98d03b8c762684d899623c257976f3eb0c9d557ff865d20cddc9d6b"
"sha256:e37707ec4fab115804670e0fb7aedb4b57075a8b6f80052bdc648d3c005184e5"
],
"markers": "python_version >= '3.6'",
"version": "==2.1.0"
"version": "==2.2.0"
},
"msgpack": {
"hashes": [
@ -900,39 +908,39 @@
},
"pycryptodome": {
"hashes": [
"sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0",
"sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d",
"sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce",
"sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06",
"sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35",
"sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27",
"sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129",
"sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9",
"sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673",
"sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1",
"sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6",
"sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8",
"sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c",
"sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713",
"sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6",
"sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438",
"sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e",
"sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07",
"sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6",
"sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd",
"sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6",
"sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8",
"sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427",
"sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067",
"sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8",
"sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b",
"sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa",
"sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf",
"sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da",
"sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"
"sha256:04e14c732c3693d2830839feed5129286ce47ffa8bfe90e4ae042c773e51c677",
"sha256:11d3164fb49fdee000fde05baecce103c0c698168ef1a18d9c7429dd66f0f5bb",
"sha256:217dcc0c92503f7dd4b3d3b7d974331a4419f97f555c99a845c3b366fed7056b",
"sha256:24c1b7705d19d8ae3e7255431efd2e526006855df62620118dd7b5374c6372f6",
"sha256:309529d2526f3fb47102aeef376b3459110a6af7efb162e860b32e3a17a46f06",
"sha256:3a153658d97258ca20bf18f7fe31c09cc7c558b6f8974a6ec74e19f6c634bd64",
"sha256:3f9fb499e267039262569d08658132c9cd8b136bf1d8c56b72f70ed05551e526",
"sha256:3faa6ebd35c61718f3f8862569c1f38450c24f3ededb213e1a64806f02f584bc",
"sha256:40083b0d7f277452c7f2dd4841801f058cc12a74c219ee4110d65774c6a58bef",
"sha256:49e54f2245befb0193848c8c8031d8d1358ed4af5a1ae8d0a3ba669a5cdd3a72",
"sha256:4e8fc4c48365ce8a542fe48bf1360da05bb2851df12f64fc94d751705e7cdbe7",
"sha256:54d4e4d45f349d8c4e2f31c2734637ff62a844af391b833f789da88e43a8f338",
"sha256:66301e4c42dee43ee2da256625d3fe81ef98cc9924c2bd535008cc3ad8ded77b",
"sha256:6b45fcace5a5d9c57ba87cf804b161adc62aa826295ce7f7acbcbdc0df74ed37",
"sha256:7efec2418e9746ec48e264eea431f8e422d931f71c57b1c96ee202b117f58fa9",
"sha256:851e6d4930b160417235955322db44adbdb19589918670d63f4acd5d92959ac0",
"sha256:8e82524e7c354033508891405574d12e612cc4fdd3b55d2c238fc1a3e300b606",
"sha256:8ec154ec445412df31acf0096e7f715e30e167c8f2318b8f5b1ab7c28f4c82f7",
"sha256:91ba4215a1f37d0f371fe43bc88c5ff49c274849f3868321c889313787de7672",
"sha256:97e7df67a4da2e3f60612bbfd6c3f243a63a15d8f4797dd275e1d7b44a65cb12",
"sha256:9a2312440057bf29b9582f72f14d79692044e63bfbc4b4bbea8559355f44f3dd",
"sha256:a7471646d8cd1a58bb696d667dcb3853e5c9b341b68dcf3c3cc0893d0f98ca5f",
"sha256:ac3012c36633564b2b5539bb7c6d9175f31d2ce74844e9abe654c428f02d0fd8",
"sha256:b1daf251395af7336ddde6a0015ba5e632c18fe646ba930ef87402537358e3b4",
"sha256:b217b4525e60e1af552d62bec01b4685095436d4de5ecde0f05d75b2f95ba6d4",
"sha256:c61ea053bd5d4c12a063d7e704fbe1c45abb5d2510dab55bd95d166ba661604f",
"sha256:c6469d1453f5864e3321a172b0aa671b938d753cbf2376b99fa2ab8841539bb8",
"sha256:cefe6b267b8e5c3c72e11adec35a9c7285b62e8ea141b63e87055e9a9e5f2f8c",
"sha256:d713dc0910e5ded07852a05e9b75f1dd9d3a31895eebee0668f612779b2a748c",
"sha256:db15fa07d2a4c00beeb5e9acdfdbc1c79f9ccfbdc1a8f36c82c4aa44951b33c9"
],
"index": "pypi",
"version": "==3.10.1"
"version": "==3.10.4"
},
"pyjwt": {
"hashes": [
@ -1084,11 +1092,11 @@
},
"sentry-sdk": {
"hashes": [
"sha256:494bf9990a54400710b7b6eb7d8174fc89039a4c82feedcf498d800d54e2ee81",
"sha256:963326914aa219ad5abda5b479b871fed2b3d2ddc9d27037340637647c9a4cdb"
"sha256:4297555ddc37c7136740e6b547b7d68f5bca0b4832f94ac097e5d531a4c77528",
"sha256:ea04bc3be6eb082f34ff3f8f6380ea9c691766592298f3f975a435dafac6bf6a"
],
"index": "pypi",
"version": "==1.4.0"
"version": "==1.4.1"
},
"service-identity": {
"hashes": [
@ -1178,11 +1186,11 @@
"secure"
],
"hashes": [
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
"sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
"sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
],
"index": "pypi",
"version": "==1.26.6"
"version": "==1.26.7"
},
"uvicorn": {
"extras": [
@ -1574,7 +1582,7 @@
"sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899",
"sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"
],
"markers": "python_version < '4' and python_full_version >= '3.6.1'",
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
"version": "==5.9.3"
},
"lazy-object-proxy": {
@ -1644,11 +1652,11 @@
},
"platformdirs": {
"hashes": [
"sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f",
"sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"
"sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2",
"sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"
],
"markers": "python_version >= '3.6'",
"version": "==2.3.0"
"version": "==2.4.0"
},
"pluggy": {
"hashes": [
@ -1750,49 +1758,49 @@
},
"regex": {
"hashes": [
"sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468",
"sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354",
"sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308",
"sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d",
"sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc",
"sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8",
"sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797",
"sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2",
"sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13",
"sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d",
"sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a",
"sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0",
"sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73",
"sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1",
"sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed",
"sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a",
"sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b",
"sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f",
"sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256",
"sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb",
"sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2",
"sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983",
"sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb",
"sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645",
"sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8",
"sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a",
"sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906",
"sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f",
"sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c",
"sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892",
"sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0",
"sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e",
"sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e",
"sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed",
"sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c",
"sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374",
"sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd",
"sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791",
"sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a",
"sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1",
"sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"
"sha256:0628ed7d6334e8f896f882a5c1240de8c4d9b0dd7c7fb8e9f4692f5684b7d656",
"sha256:09eb62654030f39f3ba46bc6726bea464069c29d00a9709e28c9ee9623a8da4a",
"sha256:0bba1f6df4eafe79db2ecf38835c2626dbd47911e0516f6962c806f83e7a99ae",
"sha256:10a7a9cbe30bd90b7d9a1b4749ef20e13a3528e4215a2852be35784b6bd070f0",
"sha256:17310b181902e0bb42b29c700e2c2346b8d81f26e900b1328f642e225c88bce1",
"sha256:1e8d1898d4fb817120a5f684363b30108d7b0b46c7261264b100d14ec90a70e7",
"sha256:2054dea683f1bda3a804fcfdb0c1c74821acb968093d0be16233873190d459e3",
"sha256:29385c4dbb3f8b3a55ce13de6a97a3d21bd00de66acd7cdfc0b49cb2f08c906c",
"sha256:295bc8a13554a25ad31e44c4bedabd3c3e28bba027e4feeb9bb157647a2344a7",
"sha256:2cdb3789736f91d0b3333ac54d12a7e4f9efbc98f53cb905d3496259a893a8b3",
"sha256:3baf3eaa41044d4ced2463fd5d23bf7bd4b03d68739c6c99a59ce1f95599a673",
"sha256:4e61100200fa6ab7c99b61476f9f9653962ae71b931391d0264acfb4d9527d9c",
"sha256:6266fde576e12357b25096351aac2b4b880b0066263e7bc7a9a1b4307991bb0e",
"sha256:650c4f1fc4273f4e783e1d8e8b51a3e2311c2488ba0fcae6425b1e2c248a189d",
"sha256:658e3477676009083422042c4bac2bdad77b696e932a3de001c42cc046f8eda2",
"sha256:6adc1bd68f81968c9d249aab8c09cdc2cbe384bf2d2cb7f190f56875000cdc72",
"sha256:6c4d83d21d23dd854ffbc8154cf293f4e43ba630aa9bd2539c899343d7f59da3",
"sha256:6f74b6d8f59f3cfb8237e25c532b11f794b96f5c89a6f4a25857d85f84fbef11",
"sha256:7783d89bd5413d183a38761fbc68279b984b9afcfbb39fa89d91f63763fbfb90",
"sha256:7e3536f305f42ad6d31fc86636c54c7dafce8d634e56fef790fbacb59d499dd5",
"sha256:821e10b73e0898544807a0692a276e539e5bafe0a055506a6882814b6a02c3ec",
"sha256:835962f432bce92dc9bf22903d46c50003c8d11b1dc64084c8fae63bca98564a",
"sha256:85c61bee5957e2d7be390392feac7e1d7abd3a49cbaed0c8cee1541b784c8561",
"sha256:86f9931eb92e521809d4b64ec8514f18faa8e11e97d6c2d1afa1bcf6c20a8eab",
"sha256:8a5c2250c0a74428fd5507ae8853706fdde0f23bfb62ee1ec9418eeacf216078",
"sha256:8aec4b4da165c4a64ea80443c16e49e3b15df0f56c124ac5f2f8708a65a0eddc",
"sha256:8c268e78d175798cd71d29114b0a1f1391c7d011995267d3b62319ec1a4ecaa1",
"sha256:8d80087320632457aefc73f686f66139801959bf5b066b4419b92be85be3543c",
"sha256:95e89a8558c8c48626dcffdf9c8abac26b7c251d352688e7ab9baf351e1c7da6",
"sha256:9c371dd326289d85906c27ec2bc1dcdedd9d0be12b543d16e37bad35754bde48",
"sha256:9c7cb25adba814d5f419733fe565f3289d6fa629ab9e0b78f6dff5fa94ab0456",
"sha256:a731552729ee8ae9c546fb1c651c97bf5f759018fdd40d0e9b4d129e1e3a44c8",
"sha256:aea4006b73b555fc5bdb650a8b92cf486d678afa168cf9b38402bb60bf0f9c18",
"sha256:b0e3f59d3c772f2c3baaef2db425e6fc4149d35a052d874bb95ccfca10a1b9f4",
"sha256:b15dc34273aefe522df25096d5d087abc626e388a28a28ac75a4404bb7668736",
"sha256:c000635fd78400a558bd7a3c2981bb2a430005ebaa909d31e6e300719739a949",
"sha256:c31f35a984caffb75f00a86852951a337540b44e4a22171354fb760cefa09346",
"sha256:c50a6379763c733562b1fee877372234d271e5c78cd13ade5f25978aa06744db",
"sha256:c94722bf403b8da744b7d0bb87e1f2529383003ceec92e754f768ef9323f69ad",
"sha256:dcbbc9cfa147d55a577d285fd479b43103188855074552708df7acc31a476dd9",
"sha256:fb9f5844db480e2ef9fce3a72e71122dd010ab7b2920f777966ba25f7eb63819"
],
"version": "==2021.8.28"
"version": "==2021.9.24"
},
"requests": {
"hashes": [
@ -1863,11 +1871,11 @@
"secure"
],
"hashes": [
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
"sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece",
"sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"
],
"index": "pypi",
"version": "==1.26.6"
"version": "==1.26.7"
},
"wrapt": {
"hashes": [

View File

@ -1,3 +1,3 @@
"""authentik"""
__version__ = "2021.9.1"
__version__ = "2021.9.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -84,7 +84,7 @@ class SystemSerializer(PassiveSerializer):
return now()
def get_embedded_outpost_host(self, request: Request) -> str:
"""Get the FQDN configured on the embeddded outpost"""
"""Get the FQDN configured on the embedded outpost"""
outposts = Outpost.objects.filter(managed=MANAGED_OUTPOST)
if not outposts.exists():
return ""

View File

@ -11,7 +11,7 @@ from drf_spectacular.types import OpenApiTypes
def build_standard_type(obj, **kwargs):
"""Build a basic type with optional add ons."""
"""Build a basic type with optional add owns."""
schema = build_basic_type(obj)
schema.update(kwargs)
return schema

View File

@ -63,7 +63,7 @@ class ConfigView(APIView):
@extend_schema(responses={200: ConfigSerializer(many=False)})
def get(self, request: Request) -> Response:
"""Retrive public configuration options"""
"""Retrieve public configuration options"""
config = ConfigSerializer(
{
"error_reporting_enabled": CONFIG.y("error_reporting.enabled"),

View File

@ -10,10 +10,13 @@ from rest_framework.permissions import AllowAny
from rest_framework.request import Request
from rest_framework.throttling import AnonRateThrottle
from rest_framework.views import APIView
from structlog.stdlib import get_logger
from authentik.api.tasks import sentry_proxy
from authentik.lib.config import CONFIG
LOGGER = get_logger()
class PlainTextParser(BaseParser):
"""Plain text parser."""
@ -45,6 +48,7 @@ class SentryTunnelView(APIView):
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
# Only allow usage of this endpoint when error reporting is enabled
if not CONFIG.y_bool("error_reporting.enabled", False):
LOGGER.debug("error reporting disabled")
return HttpResponse(status=400)
# Body is 2 json objects separated by \n
full_body = request.body
@ -55,6 +59,7 @@ class SentryTunnelView(APIView):
# Check that the DSN is what we expect
dsn = header.get("dsn", "")
if dsn != settings.SENTRY_DSN:
LOGGER.debug("Invalid dsn", have=dsn, expected=settings.SENTRY_DSN)
return HttpResponse(status=400)
sentry_proxy.delay(full_body.decode())
return HttpResponse(status=204)

View File

@ -308,7 +308,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
"""Allow users to change information on their own profile"""
data = UserSelfSerializer(instance=User.objects.get(pk=request.user.pk), data=request.data)
if not data.is_valid():
return Response(data.errors)
return Response(data.errors, status=400)
new_user = data.save()
# If we're impersonating, we need to update that user object
# since it caches the full object

View File

@ -26,7 +26,7 @@ class Migration(migrations.Migration):
),
(
"username_link",
"Link to a user with identical username address. Can have security implications when a username is used with another source.",
"Link to a user with identical username. Can have security implications when a username is used with another source.",
),
(
"username_deny",

View File

@ -283,7 +283,7 @@ class SourceUserMatchingModes(models.TextChoices):
)
USERNAME_LINK = "username_link", _(
(
"Link to a user with identical username address. Can have security implications "
"Link to a user with identical username. Can have security implications "
"when a username is used with another source."
)
)

View File

@ -1,7 +1,10 @@
"""NotificationTransport API Views"""
from typing import Any
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
from rest_framework.fields import CharField, ListField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
@ -29,6 +32,14 @@ class NotificationTransportSerializer(ModelSerializer):
"""Return selected mode with a UI Label"""
return TransportMode(instance.mode).label
def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
"""Ensure the required fields are set."""
mode = attrs.get("mode")
if mode in [TransportMode.WEBHOOK, TransportMode.WEBHOOK_SLACK]:
if "webhook_url" not in attrs or attrs.get("webhook_url", "") == "":
raise ValidationError("Webhook URL may not be empty.")
return attrs
class Meta:
model = NotificationTransport

View File

@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Optional, Type, Union
from uuid import uuid4
from django.conf import settings
from django.core.validators import URLValidator
from django.db import models
from django.http import HttpRequest
from django.http.request import QueryDict
@ -223,7 +224,7 @@ class NotificationTransport(models.Model):
name = models.TextField(unique=True)
mode = models.TextField(choices=TransportMode.choices)
webhook_url = models.TextField(blank=True)
webhook_url = models.TextField(blank=True, validators=[URLValidator()])
webhook_mapping = models.ForeignKey(
"NotificationWebhookMapping", on_delete=models.SET_DEFAULT, null=True, default=None
)

View File

@ -4,7 +4,13 @@ from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.events.models import Event, EventAction, Notification, NotificationSeverity
from authentik.events.models import (
Event,
EventAction,
Notification,
NotificationSeverity,
TransportMode,
)
class TestEventsAPI(APITestCase):
@ -41,3 +47,23 @@ class TestEventsAPI(APITestCase):
)
notification.refresh_from_db()
self.assertTrue(notification.seen)
def test_transport(self):
"""Test transport API"""
response = self.client.post(
reverse("authentik_api:notificationtransport-list"),
data={
"name": "foo-with",
"mode": TransportMode.WEBHOOK,
"webhook_url": "http://foo.com",
},
)
self.assertEqual(response.status_code, 201)
response = self.client.post(
reverse("authentik_api:notificationtransport-list"),
data={
"name": "foo-without",
"mode": TransportMode.WEBHOOK,
},
)
self.assertEqual(response.status_code, 400)

View File

@ -77,7 +77,7 @@ def sanitize_dict(source: dict[Any, Any]) -> dict[Any, Any]:
final_dict = {}
for key, value in source.items():
if is_dataclass(value):
# Because asdict calls `copy.deepcopy(obj)` on everything thats not tuple/dict,
# Because asdict calls `copy.deepcopy(obj)` on everything that's not tuple/dict,
# and deepcopy doesn't work with HttpRequests (neither django nor rest_framework).
# Currently, the only dataclass that actually holds an http request is a PolicyRequest
if isinstance(value, PolicyRequest):

View File

@ -57,11 +57,11 @@ class FlowPlan:
markers: list[StageMarker] = field(default_factory=list)
def append_stage(self, stage: Stage, marker: Optional[StageMarker] = None):
"""Append `stage` to all stages, optionall with stage marker"""
"""Append `stage` to all stages, optionally with stage marker"""
return self.append(FlowStageBinding(stage=stage), marker)
def append(self, binding: FlowStageBinding, marker: Optional[StageMarker] = None):
"""Append `stage` to all stages, optionall with stage marker"""
"""Append `stage` to all stages, optionally with stage marker"""
self.bindings.append(binding)
self.markers.append(marker or StageMarker())

View File

@ -438,7 +438,7 @@ class TestFlowExecutor(APITestCase):
# third request, this should trigger the re-evaluate
# A get request will evaluate the policies and this will return stage 4
# but it won't save it, hence we cant' check the plan
# but it won't save it, hence we can't check the plan
response = self.client.get(exec_url)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(

View File

@ -11,7 +11,7 @@ from authentik.lib.sentry import SentryIgnoredException
def get_attrs(obj: SerializerModel) -> dict[str, Any]:
"""Get object's attributes via their serializer, and covert it to a normal dict"""
"""Get object's attributes via their serializer, and convert it to a normal dict"""
data = dict(obj.serializer(obj).data)
to_remove = (
"policies",

View File

@ -126,12 +126,12 @@ class FlowExecutorView(APIView):
# pylint: disable=unused-argument, too-many-return-statements
def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse:
# Early check if theres an active Plan for the current session
# Early check if there's an active Plan for the current session
if SESSION_KEY_PLAN in self.request.session:
self.plan = self.request.session[SESSION_KEY_PLAN]
if self.plan.flow_pk != self.flow.pk.hex:
self._logger.warning(
"f(exec): Found existing plan for other flow, deleteing plan",
"f(exec): Found existing plan for other flow, deleting plan",
)
# Existing plan is deleted from session and instance
self.plan = None
@ -433,7 +433,7 @@ class ToDefaultFlow(View):
plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
if plan.flow_pk != flow.pk.hex:
LOGGER.warning(
"f(def): Found existing plan for other flow, deleteing plan",
"f(def): Found existing plan for other flow, deleting plan",
flow_slug=flow.slug,
)
del self.request.session[SESSION_KEY_PLAN]

View File

@ -32,7 +32,7 @@ class TestConfig(TestCase):
config = ConfigLoader()
environ["foo"] = "bar"
self.assertEqual(config.parse_uri("env://foo"), "bar")
self.assertEqual(config.parse_uri("env://fo?bar"), "bar")
self.assertEqual(config.parse_uri("env://foo?bar"), "bar")
def test_uri_file(self):
"""Test URI parsing (file load)"""

View File

@ -27,7 +27,7 @@ class TestHTTP(TestCase):
token = Token.objects.create(
identifier="test", user=self.user, intent=TokenIntents.INTENT_API
)
# Invalid, non-existant token
# Invalid, non-existent token
request = self.factory.get(
"/",
**{
@ -36,7 +36,7 @@ class TestHTTP(TestCase):
},
)
self.assertEqual(get_client_ip(request), "127.0.0.1")
# Invalid, user doesn't have permisions
# Invalid, user doesn't have permissions
request = self.factory.get(
"/",
**{

View File

@ -104,7 +104,7 @@ class OutpostConsumer(AuthJsonConsumer):
expected=self.outpost.config.kubernetes_replicas,
).inc()
LOGGER.debug(
"added outpost instace to cache",
"added outpost instance to cache",
outpost=self.outpost,
instance_uuid=self.last_uid,
)

View File

@ -38,6 +38,7 @@ class DockerController(BaseController):
"AUTHENTIK_HOST": self.outpost.config.authentik_host.lower(),
"AUTHENTIK_INSECURE": str(self.outpost.config.authentik_host_insecure).lower(),
"AUTHENTIK_TOKEN": self.outpost.token.key,
"AUTHENTIK_HOST_BROWSER": self.outpost.config.authentik_host_browser,
}
def _comp_env(self, container: Container) -> bool:
@ -215,6 +216,7 @@ class DockerController(BaseController):
"AUTHENTIK_HOST": self.outpost.config.authentik_host,
"AUTHENTIK_INSECURE": str(self.outpost.config.authentik_host_insecure),
"AUTHENTIK_TOKEN": self.outpost.token.key,
"AUTHENTIK_HOST_BROWSER": self.outpost.config.authentik_host_browser,
},
"labels": self._get_labels(),
}

View File

@ -109,7 +109,7 @@ class KubernetesObjectReconciler(Generic[T]):
except (OpenApiException, HTTPError) as exc:
# pylint: disable=no-member
if isinstance(exc, ApiException) and exc.status == 404:
self.logger.debug("Failed to get current, assuming non-existant")
self.logger.debug("Failed to get current, assuming non-existent")
return
self.logger.debug("Other unhandled error", exc=exc)
raise exc
@ -129,7 +129,7 @@ class KubernetesObjectReconciler(Generic[T]):
raise NotImplementedError
def retrieve(self) -> T:
"""API Wrapper to retrive object"""
"""API Wrapper to retrieve object"""
raise NotImplementedError
def delete(self, reference: T):

View File

@ -89,6 +89,15 @@ class DeploymentReconciler(KubernetesObjectReconciler[V1Deployment]):
)
),
),
V1EnvVar(
name="AUTHENTIK_HOST_BROWSER",
value_from=V1EnvVarSource(
secret_key_ref=V1SecretKeySelector(
name=self.name,
key="authentik_host_browser",
)
),
),
V1EnvVar(
name="AUTHENTIK_TOKEN",
value_from=V1EnvVarSource(

View File

@ -26,7 +26,7 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
def reconcile(self, current: V1Secret, reference: V1Secret):
super().reconcile(current, reference)
for key in reference.data.keys():
if current.data[key] != reference.data[key]:
if key not in current.data or current.data[key] != reference.data[key]:
raise NeedsUpdate()
def get_reference_object(self) -> V1Secret:
@ -40,6 +40,9 @@ class SecretReconciler(KubernetesObjectReconciler[V1Secret]):
str(self.controller.outpost.config.authentik_host_insecure)
),
"token": b64string(self.controller.outpost.token.key),
"authentik_host_browser": b64string(
self.controller.outpost.config.authentik_host_browser
),
},
)

View File

@ -19,12 +19,16 @@ class ServiceReconciler(KubernetesObjectReconciler[V1Service]):
self.api = CoreV1Api(controller.client)
def reconcile(self, current: V1Service, reference: V1Service):
super().reconcile(current, reference)
if len(current.spec.ports) != len(reference.spec.ports):
raise NeedsRecreate()
for port in reference.spec.ports:
if port not in current.spec.ports:
raise NeedsRecreate()
# run the base reconcile last, as that will probably raise NeedsUpdate
# after an authentik update. However the ports might have also changed during
# the update, so this causes the service to be re-created with higher
# priority than being updated.
super().reconcile(current, reference)
def get_reference_object(self) -> V1Service:
"""Get deployment object for outpost"""

View File

@ -30,7 +30,7 @@ class DockerInlineTLS:
return str(path)
def write(self) -> TLSConfig:
"""Create TLSConfig with Certificate Keypairs"""
"""Create TLSConfig with Certificate Key pairs"""
# So yes, this is quite ugly. But sadly, there is no clean way to pass
# docker-py (which is using requests (which is using urllib3)) a certificate
# for verification or authentication as string.

View File

@ -64,6 +64,7 @@ class OutpostConfig:
authentik_host: str = ""
authentik_host_insecure: bool = False
authentik_host_browser: str = ""
log_level: str = CONFIG.y("log_level")
error_reporting_enabled: bool = CONFIG.y_bool("error_reporting.enabled")

View File

@ -181,7 +181,7 @@ def outpost_post_save(model_class: str, model_pk: Any):
def outpost_send_update(model_instace: Model):
"""Send outpost update to all registered outposts, irregardless to which authentik
"""Send outpost update to all registered outposts, regardless to which authentik
instance they are connected"""
channel_layer = get_channel_layer()
if isinstance(model_instace, OutpostModel):
@ -208,7 +208,7 @@ def _outpost_single_update(outpost: Outpost, layer=None):
@CELERY_APP.task()
def outpost_local_connection():
"""Checks the local environment and create Service connections."""
# Explicitly check against token filename, as thats
# Explicitly check against token filename, as that's
# only present when the integration is enabled
if Path(SERVICE_TOKEN_FILENAME).exists():
LOGGER.debug("Detected in-cluster Kubernetes Config")

View File

@ -46,7 +46,7 @@ def cache_key(binding: PolicyBinding, request: PolicyRequest) -> str:
class PolicyProcess(PROCESS_CLASS):
"""Evaluate a single policy within a seprate process"""
"""Evaluate a single policy within a separate process"""
connection: Connection
binding: PolicyBinding

View File

@ -34,7 +34,7 @@ def update_score(request: HttpRequest, username: str, amount: int):
@receiver(user_login_failed)
# pylint: disable=unused-argument
def handle_failed_login(sender, request, credentials, **_):
"""Lower Score for failed loging attempts"""
"""Lower Score for failed login attempts"""
if "username" in credentials:
update_score(request, credentials.get("username"), -1)

View File

@ -14,7 +14,7 @@ from authentik.policies.types import PolicyRequest
def clear_policy_cache():
"""Ensure no policy-related keys are stil cached"""
"""Ensure no policy-related keys are still cached"""
keys = cache.keys("policy_*")
cache.delete(keys)

View File

@ -1,11 +1,11 @@
"""LDAP Provider Docker Contoller"""
"""LDAP Provider Docker Controller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.docker import DockerController
from authentik.outposts.models import DockerServiceConnection, Outpost
class LDAPDockerController(DockerController):
"""LDAP Provider Docker Contoller"""
"""LDAP Provider Docker Controller"""
def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
super().__init__(outpost, connection)

View File

@ -1,11 +1,11 @@
"""LDAP Provider Kubernetes Contoller"""
"""LDAP Provider Kubernetes Controller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost
class LDAPKubernetesController(KubernetesController):
"""LDAP Provider Kubernetes Contoller"""
"""LDAP Provider Kubernetes Controller"""
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
super().__init__(outpost, connection)

View File

@ -153,7 +153,7 @@ def protected_resource_view(scopes: list[str]):
if not set(scopes).issubset(set(token.scope)):
LOGGER.warning(
"Scope missmatch.",
"Scope mismatch.",
required=set(scopes),
token_has=set(token.scope),
)

View File

@ -33,7 +33,7 @@ class UserInfoView(View):
for scope in ScopeMapping.objects.filter(scope_name__in=scopes).order_by("scope_name"):
if scope.description != "":
scope_descriptions.append({"id": scope.scope_name, "name": scope.description})
# GitHub Compatibility Scopes are handeled differently, since they required custom paths
# GitHub Compatibility Scopes are handled differently, since they required custom paths
# Hence they don't exist as Scope objects
github_scope_map = {
SCOPE_GITHUB_USER: ("GitHub Compatibility: Access your User Information"),

View File

@ -1,5 +1,5 @@
"""ProxyProvider API Views"""
from typing import Any
from typing import Any, Optional
from drf_spectacular.utils import extend_schema_field
from rest_framework.exceptions import ValidationError
@ -10,6 +10,7 @@ from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import PassiveSerializer
from authentik.lib.utils.time import timedelta_from_string
from authentik.providers.oauth2.views.provider import ProviderInfoView
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
@ -106,6 +107,16 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
"""Proxy provider serializer for outposts"""
oidc_configuration = SerializerMethodField()
token_validity = SerializerMethodField()
@extend_schema_field(OpenIDConnectConfigurationSerializer)
def get_oidc_configuration(self, obj: ProxyProvider):
"""Embed OpenID Connect provider information"""
return ProviderInfoView(request=self.context["request"]._request).get_info(obj)
def get_token_validity(self, obj: ProxyProvider) -> Optional[float]:
"""Get token validity as second count"""
return timedelta_from_string(obj.token_validity).total_seconds()
class Meta:
@ -127,13 +138,9 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
"basic_auth_user_attribute",
"mode",
"cookie_domain",
"token_validity",
]
@extend_schema_field(OpenIDConnectConfigurationSerializer)
def get_oidc_configuration(self, obj: ProxyProvider):
"""Embed OpenID Connect provider information"""
return ProviderInfoView(request=self.context["request"]._request).get_info(obj)
class ProxyOutpostConfigViewSet(ReadOnlyModelViewSet):
"""ProxyProvider Viewset"""

View File

@ -1,4 +1,4 @@
"""Proxy Provider Docker Contoller"""
"""Proxy Provider Docker Controller"""
from urllib.parse import urlparse
from authentik.outposts.controllers.base import DeploymentPort
@ -8,7 +8,7 @@ from authentik.providers.proxy.models import ProxyProvider
class ProxyDockerController(DockerController):
"""Proxy Provider Docker Contoller"""
"""Proxy Provider Docker Controller"""
def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
super().__init__(outpost, connection)
@ -29,6 +29,11 @@ class ProxyDockerController(DockerController):
labels[f"traefik.http.routers.{traefik_name}-router.rule"] = f"Host({','.join(hosts)})"
labels[f"traefik.http.routers.{traefik_name}-router.tls"] = "true"
labels[f"traefik.http.routers.{traefik_name}-router.service"] = f"{traefik_name}-service"
labels[f"traefik.http.services.{traefik_name}-service.loadbalancer.healthcheck.path"] = "/"
labels[
f"traefik.http.services.{traefik_name}-service.loadbalancer.healthcheck.path"
] = "/akprox/ping"
labels[
f"traefik.http.services.{traefik_name}-service.loadbalancer.healthcheck.port"
] = "9300"
labels[f"traefik.http.services.{traefik_name}-service.loadbalancer.server.port"] = "9000"
return labels

View File

@ -1,4 +1,4 @@
"""Proxy Provider Kubernetes Contoller"""
"""Proxy Provider Kubernetes Controller"""
from authentik.outposts.controllers.base import DeploymentPort
from authentik.outposts.controllers.kubernetes import KubernetesController
from authentik.outposts.models import KubernetesServiceConnection, Outpost
@ -7,7 +7,7 @@ from authentik.providers.proxy.controllers.k8s.traefik import TraefikMiddlewareR
class ProxyKubernetesController(KubernetesController):
"""Proxy Provider Kubernetes Contoller"""
"""Proxy Provider Kubernetes Controller"""
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
super().__init__(outpost, connection)

View File

@ -119,7 +119,7 @@ class LDAPPasswordChanger:
return True
def ad_password_complexity(self, password: str, user: Optional[User] = None) -> bool:
"""Check if password matches Active direcotry password policies
"""Check if password matches Active directory password policies
https://docs.microsoft.com/en-us/windows/security/threat-protection/
security-policy-settings/password-must-meet-complexity-requirements

View File

@ -48,7 +48,7 @@ def ldap_password_validate(sender, password: str, plan_context: dict[str, Any],
password, plan_context.get(PLAN_CONTEXT_PENDING_USER, None)
)
if not passing:
raise ValidationError(_("Password does not match Active Direcory Complexity."))
raise ValidationError(_("Password does not match Active Directory Complexity."))
@receiver(password_changed)

View File

@ -46,9 +46,9 @@ class OAuthSourceSerializer(SourceSerializer):
type = SerializerMethodField()
@extend_schema_field(SourceTypeSerializer)
def get_type(self, instace: OAuthSource) -> SourceTypeSerializer:
def get_type(self, instance: OAuthSource) -> SourceTypeSerializer:
"""Get source's type configuration"""
return SourceTypeSerializer(instace.type).data
return SourceTypeSerializer(instance.type).data
def validate(self, attrs: dict) -> dict:
provider_type = MANAGER.find_type(attrs.get("provider_type", ""))

View File

@ -14,7 +14,7 @@ class Migration(migrations.Migration):
model_name="oauthsource",
name="access_token_url",
field=models.CharField(
help_text="URL used by authentik to retrive tokens.",
help_text="URL used by authentik to retrieve tokens.",
max_length=255,
verbose_name="Access Token URL",
),

View File

@ -15,7 +15,7 @@ class Migration(migrations.Migration):
name="access_token_url",
field=models.CharField(
blank=True,
help_text="URL used by authentik to retrive tokens.",
help_text="URL used by authentik to retrieve tokens.",
max_length=255,
verbose_name="Access Token URL",
),

View File

@ -39,7 +39,7 @@ class Migration(migrations.Migration):
model_name="oauthsource",
name="access_token_url",
field=models.CharField(
help_text="URL used by authentik to retrive tokens.",
help_text="URL used by authentik to retrieve tokens.",
max_length=255,
null=True,
verbose_name="Access Token URL",

View File

@ -37,7 +37,7 @@ class OAuthSource(Source):
max_length=255,
null=True,
verbose_name=_("Access Token URL"),
help_text=_("URL used by authentik to retrive tokens."),
help_text=_("URL used by authentik to retrieve tokens."),
)
profile_url = models.CharField(
max_length=255,

View File

@ -24,7 +24,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name="plexsource",
name="plex_token",
field=models.TextField(default="", help_text="Plex token used to check firends"),
field=models.TextField(default="", help_text="Plex token used to check friends"),
),
migrations.AlterField(
model_name="plexsource",

View File

@ -13,6 +13,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name="plexsource",
name="plex_token",
field=models.TextField(help_text="Plex token used to check firends"),
field=models.TextField(help_text="Plex token used to check friends"),
),
]

View File

@ -50,7 +50,7 @@ class PlexSource(Source):
default=True,
help_text=_("Allow friends to authenticate, even if you don't share a server."),
)
plex_token = models.TextField(help_text=_("Plex token used to check firends"))
plex_token = models.TextField(help_text=_("Plex token used to check friends"))
@property
def component(self) -> str:

View File

@ -54,7 +54,7 @@ class CaptchaChallengeResponse(ChallengeResponse):
class CaptchaStageView(ChallengeStageView):
"""Simple captcha checker, logic is handeled in django-captcha module"""
"""Simple captcha checker, logic is handled in django-captcha module"""
response_class = CaptchaChallengeResponse

View File

@ -53,9 +53,11 @@ class PromptChallengeResponse(ChallengeResponse):
def __init__(self, *args, **kwargs):
stage: PromptStage = kwargs.pop("stage", None)
plan: FlowPlan = kwargs.pop("plan", None)
request: HttpRequest = kwargs.pop("request", None)
super().__init__(*args, **kwargs)
self.stage = stage
self.plan = plan
self.request = request
if not self.stage:
return
# list() is called so we only load the fields once
@ -104,8 +106,9 @@ class PromptChallengeResponse(ChallengeResponse):
self._validate_password_fields(*[field.field_key for field in password_fields])
user = self.plan.context.get(PLAN_CONTEXT_PENDING_USER, get_anonymous_user())
engine = ListPolicyEngine(self.stage.validation_policies.all(), user)
engine.request.context = attrs
engine = ListPolicyEngine(self.stage.validation_policies.all(), user, self.request)
engine.request.context[PLAN_CONTEXT_PROMPT] = attrs
engine.request.context.update(attrs)
engine.build()
result = engine.result
if not result.passing:
@ -173,6 +176,7 @@ class PromptStageView(ChallengeStageView):
return PromptChallengeResponse(
instance=None,
data=data,
request=self.request,
stage=self.executor.current_stage,
plan=self.executor.plan,
)

View File

@ -21,7 +21,7 @@ services:
networks:
- internal
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.9.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.9.3}
restart: unless-stopped
command: server
environment:
@ -44,7 +44,7 @@ services:
- "0.0.0.0:9000:9000"
- "0.0.0.0:9443:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.9.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.9.3}
restart: unless-stopped
command: worker
networks:

2
go.mod
View File

@ -34,7 +34,7 @@ require (
github.com/recws-org/recws v1.3.1
github.com/sirupsen/logrus v1.8.1
go.mongodb.org/mongo-driver v1.5.2 // indirect
goauthentik.io/api v0.202191.3
goauthentik.io/api v0.202192.5
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558

4
go.sum
View File

@ -554,8 +554,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
goauthentik.io/api v0.202191.3 h1:2qrU8j2x6qPte9GEjCmUWL1KrMZu3DByjBQBfXAFGUE=
goauthentik.io/api v0.202191.3/go.mod h1:02nnD4FRd8lu8A1+ZuzqownBgvAhdCKzqkKX8v7JMTE=
goauthentik.io/api v0.202192.5 h1:BS4E71K2uZXy1vAdGVFLJJU0KwvAkkqKg42cYv46ud0=
goauthentik.io/api v0.202192.5/go.mod h1:02nnD4FRd8lu8A1+ZuzqownBgvAhdCKzqkKX8v7JMTE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=

View File

@ -17,4 +17,4 @@ func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s (build=%s)", VERSION, BUILD())
}
const VERSION = "2021.9.1"
const VERSION = "2021.9.3"

View File

@ -116,9 +116,9 @@ func (a *APIController) OnRefresh() error {
log.WithError(err).Error("Failed to fetch outpost configuration")
return err
}
outpost := outposts.Results[0]
a.Outpost = outposts.Results[0]
log.WithField("name", outpost.Name).Debug("Fetched outpost configuration")
log.WithField("name", a.Outpost.Name).Debug("Fetched outpost configuration")
return a.Server.Refresh()
}

View File

@ -79,6 +79,11 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
}
if req.SearchRequest.Scope == ldap.ScopeBaseObject {
pi.log.Debug("base scope, showing domain info")
return pi.SearchBase(req, flags.CanSearch)
}
if !flags.CanSearch {
pi.log.Debug("User can't search, showing info about user")
return pi.SearchMe(req, flags)
@ -111,6 +116,12 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
"client": utils.GetIP(req.conn.RemoteAddr()),
}).Inc()
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter)
case "groupOfUniqueNames":
fallthrough
case "goauthentik.io/ldap/group":
fallthrough
case "goauthentik.io/ldap/virtual-group":
fallthrough
case GroupObjectClass:
wg := sync.WaitGroup{}
wg.Add(2)
@ -160,7 +171,15 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
}()
wg.Wait()
entries = append(gEntries, uEntries...)
case UserObjectClass, "":
case "":
fallthrough
case "organizationalPerson":
fallthrough
case "inetOrgPerson":
fallthrough
case "goauthentik.io/ldap/user":
fallthrough
case UserObjectClass:
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
searchReq, skip := parseFilterForUser(c.CoreApi.CoreUsersList(uapisp.Context()), parsedFilter, false)
if skip {
@ -197,7 +216,7 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
"name": {u.Name},
"displayName": {u.Name},
"mail": {*u.Email},
"objectClass": {UserObjectClass, "organizationalPerson", "goauthentik.io/ldap/user"},
"objectClass": {UserObjectClass, "organizationalPerson", "inetOrgPerson", "goauthentik.io/ldap/user"},
"uidNumber": {pi.GetUidNumber(u)},
"gidNumber": {pi.GetUidNumber(u)},
})
@ -207,9 +226,9 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
func (pi *ProviderInstance) GroupEntry(g LDAPGroup) *ldap.Entry {
attrs := AKAttrsToLDAP(g.akAttributes)
objectClass := []string{GroupObjectClass, "goauthentik.io/ldap/group"}
objectClass := []string{GroupObjectClass, "groupOfUniqueNames", "goauthentik.io/ldap/group"}
if g.isVirtualGroup {
objectClass = []string{GroupObjectClass, "goauthentik.io/ldap/group", "goauthentik.io/ldap/virtual-group"}
objectClass = append(objectClass, "goauthentik.io/ldap/virtual-group")
}
attrs = pi.ensureAttributes(attrs, map[string][]string{

View File

@ -0,0 +1,53 @@
package ldap
import (
"fmt"
"github.com/nmcclain/ldap"
"goauthentik.io/internal/constants"
)
func (pi *ProviderInstance) SearchBase(req SearchRequest, authz bool) (ldap.ServerSearchResult, error) {
dn := ""
if authz {
dn = req.SearchRequest.BaseDN
}
return ldap.ServerSearchResult{
Entries: []*ldap.Entry{
{
DN: dn,
Attributes: []*ldap.EntryAttribute{
{
Name: "distinguishedName",
Values: []string{pi.BaseDN},
},
{
Name: "objectClass",
Values: []string{"top", "domain"},
},
{
Name: "supportedLDAPVersion",
Values: []string{"3"},
},
{
Name: "namingContexts",
Values: []string{
pi.BaseDN,
pi.GroupDN,
pi.UserDN,
},
},
{
Name: "vendorName",
Values: []string{"goauthentik.io"},
},
{
Name: "vendorVersion",
Values: []string{fmt.Sprintf("authentik LDAP Outpost Version %s (build %s)", constants.VERSION, constants.BUILD())},
},
},
},
},
Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess,
}, nil
}

View File

@ -38,7 +38,7 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
SearchRequest: searchReq,
BindDN: bindDN,
conn: conn,
log: ls.log.WithField("bindDN", bindDN).WithField("requestId", rid).WithField("client", utils.GetIP(conn.RemoteAddr())).WithField("filter", searchReq.Filter).WithField("baseDN", searchReq.BaseDN),
log: ls.log.WithField("bindDN", bindDN).WithField("requestId", rid).WithField("scope", ldap.ScopeMap[searchReq.Scope]).WithField("client", utils.GetIP(conn.RemoteAddr())).WithField("filter", searchReq.Filter).WithField("baseDN", searchReq.BaseDN),
id: rid,
ctx: span.Context(),
}
@ -74,7 +74,7 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
}
for _, provider := range ls.providers {
providerBase, _ := goldap.ParseDN(provider.BaseDN)
if providerBase.AncestorOf(bd) {
if providerBase.AncestorOf(bd) || providerBase.Equal(bd) {
return provider.Search(req)
}
}

View File

@ -39,6 +39,9 @@ func ldapResolveTypeSingle(in interface{}) *string {
func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
attrList := []*ldap.EntryAttribute{}
if attrs == nil {
return attrList
}
a := attrs.(*map[string]interface{})
for attrKey, attrValue := range *a {
entry := &ldap.EntryAttribute{Name: attrKey}

View File

@ -18,7 +18,7 @@ type OIDCEndpoint struct {
func GetOIDCEndpoint(p api.ProxyOutpostConfig, authentikHost string) OIDCEndpoint {
authUrl := p.OidcConfiguration.AuthorizationEndpoint
endUrl := p.OidcConfiguration.EndSessionEndpoint
if browserHost, found := os.LookupEnv("AUTHENTIK_HOST_BROWSER"); found {
if browserHost, found := os.LookupEnv("AUTHENTIK_HOST_BROWSER"); found && browserHost != "" {
host := os.Getenv("AUTHENTIK_HOST")
authUrl = strings.ReplaceAll(authUrl, host, browserHost)
endUrl = strings.ReplaceAll(endUrl, host, browserHost)

View File

@ -18,12 +18,22 @@ func GetStore(p api.ProxyOutpostConfig) sessions.Store {
if err != nil {
panic(err)
}
if p.TokenValidity.IsSet() {
t := p.TokenValidity.Get()
// Add one to the validity to ensure we don't have a session with indefinite length
rs.Options.MaxAge = int(*t) + 1
}
rs.Options.Domain = *p.CookieDomain
log.Info("using redis session backend")
store = rs
} else {
cs := sessions.NewCookieStore([]byte(*p.CookieSecret))
cs.Options.Domain = *p.CookieDomain
if p.TokenValidity.IsSet() {
t := p.TokenValidity.Get()
// Add one to the validity to ensure we don't have a session with indefinite length
cs.Options.MaxAge = int(*t) + 1
}
log.Info("using cookie session backend")
store = cs
}

View File

@ -45,6 +45,15 @@ func (ps *ProxyServer) Handle(rw http.ResponseWriter, r *http.Request) {
host := web.GetHost(r)
a, ok := ps.apps[host]
if !ok {
// If we only have one handler, host name switching doesn't matter
if len(ps.apps) == 1 {
ps.log.WithField("host", host).Warning("passing to single app mux")
for k := range ps.apps {
ps.apps[k].ServeHTTP(rw, r)
return
}
}
ps.log.WithField("host", host).Warning("no app for hostname")
rw.WriteHeader(400)
return

View File

@ -36,13 +36,15 @@ func (ws *WebServer) configureStatic() {
}
statRouter.PathPrefix("/static/dist/").Handler(distHandler)
statRouter.PathPrefix("/static/authentik/").Handler(authentikHandler)
// Prevent font-loading issues on safari, which loads fonts relatively to the URL the browser is on
statRouter.PathPrefix("/if/flow/{flow_slug}/assets").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
http.StripPrefix(fmt.Sprintf("/if/flow/%s", vars["flow_slug"]), distFs).ServeHTTP(rw, r)
disableIndex(http.StripPrefix(fmt.Sprintf("/if/flow/%s", vars["flow_slug"]), distFs)).ServeHTTP(rw, r)
})
statRouter.PathPrefix("/if/admin/assets").Handler(http.StripPrefix("/if/admin", distFs))
statRouter.PathPrefix("/if/admin/assets").Handler(disableIndex(http.StripPrefix("/if/admin", distFs)))
statRouter.PathPrefix("/if/user/assets").Handler(disableIndex(http.StripPrefix("/if/user", distFs)))
statRouter.PathPrefix("/media/").Handler(http.StripPrefix("/media", fs))

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2021.9.1
version: 2021.9.3
description: Making authentication simple.
contact:
email: hello@beryju.org
@ -11395,7 +11395,7 @@ paths:
/root/config/:
get:
operationId: root_config_retrieve
description: Retrive public configuration options
description: Retrieve public configuration options
tags:
- root
security:
@ -21580,6 +21580,7 @@ components:
readOnly: true
webhook_url:
type: string
format: uri
webhook_mapping:
type: string
format: uuid
@ -21609,6 +21610,7 @@ components:
$ref: '#/components/schemas/NotificationTransportModeEnum'
webhook_url:
type: string
format: uri
webhook_mapping:
type: string
format: uuid
@ -21906,7 +21908,7 @@ components:
access_token_url:
type: string
nullable: true
description: URL used by authentik to retrive tokens.
description: URL used by authentik to retrieve tokens.
maxLength: 255
profile_url:
type: string
@ -21981,7 +21983,7 @@ components:
access_token_url:
type: string
nullable: true
description: URL used by authentik to retrive tokens.
description: URL used by authentik to retrieve tokens.
maxLength: 255
profile_url:
type: string
@ -25808,6 +25810,7 @@ components:
$ref: '#/components/schemas/NotificationTransportModeEnum'
webhook_url:
type: string
format: uri
webhook_mapping:
type: string
format: uuid
@ -25934,7 +25937,7 @@ components:
access_token_url:
type: string
nullable: true
description: URL used by authentik to retrive tokens.
description: URL used by authentik to retrieve tokens.
maxLength: 255
profile_url:
type: string
@ -26112,7 +26115,7 @@ components:
description: Allow friends to authenticate, even if you don't share a server.
plex_token:
type: string
description: Plex token used to check firends
description: Plex token used to check friends
PatchedPolicyBindingRequest:
type: object
description: PolicyBinding Serializer
@ -26745,7 +26748,7 @@ components:
description: Allow friends to authenticate, even if you don't share a server.
plex_token:
type: string
description: Plex token used to check firends
description: Plex token used to check friends
required:
- component
- name
@ -26840,7 +26843,7 @@ components:
description: Allow friends to authenticate, even if you don't share a server.
plex_token:
type: string
description: Plex token used to check firends
description: Plex token used to check friends
required:
- name
- plex_token
@ -27375,11 +27378,17 @@ components:
Exclusive with internal_host.
cookie_domain:
type: string
token_validity:
type: number
format: float
nullable: true
readOnly: true
required:
- external_host
- name
- oidc_configuration
- pk
- token_validity
ProxyProvider:
type: object
description: ProxyProvider Serializer

View File

@ -217,6 +217,7 @@ class TestProviderLDAP(SeleniumTestCase):
"objectClass": [
"user",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
],
"uidNumber": [str(2000 + outpost_user.pk)],
@ -243,6 +244,7 @@ class TestProviderLDAP(SeleniumTestCase):
"objectClass": [
"user",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
],
"uidNumber": [str(2000 + embedded_account.pk)],
@ -269,6 +271,7 @@ class TestProviderLDAP(SeleniumTestCase):
"objectClass": [
"user",
"organizationalPerson",
"inetOrgPerson",
"goauthentik.io/ldap/user",
],
"uidNumber": [str(2000 + USER().pk)],

View File

@ -6,3 +6,5 @@ dist
coverage
# don't lint generated code
api/
# Import order matters
poly.ts

42
web/package-lock.json generated
View File

@ -15,7 +15,7 @@
"@babel/preset-env": "^7.15.6",
"@babel/preset-typescript": "^7.15.0",
"@fortawesome/fontawesome-free": "^5.15.4",
"@goauthentik/api": "^2021.9.1-rc3-1632158454",
"@goauthentik/api": "^2021.9.2-1632578262",
"@lingui/cli": "^3.11.1",
"@lingui/core": "^3.11.1",
"@lingui/macro": "^3.11.1",
@ -50,8 +50,8 @@
"lit": "^2.0.0",
"moment": "^2.29.1",
"prettier": "^2.4.1",
"rapidoc": "^9.1.2",
"rollup": "^2.56.2",
"rapidoc": "^9.1.3",
"rollup": "^2.57.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",
@ -1691,9 +1691,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2021.9.1-rc3-1632158454",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.9.1-rc3-1632158454.tgz",
"integrity": "sha512-oE+UT0/mDcGn11J/sekgsQRg72p4R0N0CdQXD2Q2pgT/+q0zR0PIkjFWjnue4X8VR4D426WFAGp7WdnVVdeo3Q=="
"version": "2021.9.2-1632578262",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.9.2-1632578262.tgz",
"integrity": "sha512-pOFIODBsWPCDA3z+ZSp5UPDb0/cvuj6uVbDzVkPUJxF/fymgvmBcdY8MTIKz/usLhL7qvPra1h4RZWITWVxa6g=="
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
@ -6808,9 +6808,9 @@
}
},
"node_modules/rapidoc": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/rapidoc/-/rapidoc-9.1.2.tgz",
"integrity": "sha512-tW0ainfTZrCs3CklurkcCcoN0tIVilWPoy8ueUNQnt4oyu30vWjShq6+3Il3th3i/UQ1DAozHnMhWXOGKtkb4g==",
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/rapidoc/-/rapidoc-9.1.3.tgz",
"integrity": "sha512-9Mx/V/yO7hlM6mo5a6of0uJpm9rFBMFvqtr0Zw34JVIP19DN+cuYvUlB0MLrekn2sq0nvpzOW6cNkBIC1QSYYg==",
"dependencies": {
"@apitools/openapi-parser": "^0.0.9",
"base64-arraybuffer": "^1.0.1",
@ -7080,9 +7080,9 @@
}
},
"node_modules/rollup": {
"version": "2.56.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz",
"integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==",
"version": "2.57.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.57.0.tgz",
"integrity": "sha512-bKQIh1rWKofRee6mv8SrF2HdP6pea5QkwBZSMImJysFj39gQuiV8MEPBjXOCpzk3wSYp63M2v2wkWBmFC8O/rg==",
"bin": {
"rollup": "dist/bin/rollup"
},
@ -9760,9 +9760,9 @@
"integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg=="
},
"@goauthentik/api": {
"version": "2021.9.1-rc3-1632158454",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.9.1-rc3-1632158454.tgz",
"integrity": "sha512-oE+UT0/mDcGn11J/sekgsQRg72p4R0N0CdQXD2Q2pgT/+q0zR0PIkjFWjnue4X8VR4D426WFAGp7WdnVVdeo3Q=="
"version": "2021.9.2-1632578262",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.9.2-1632578262.tgz",
"integrity": "sha512-pOFIODBsWPCDA3z+ZSp5UPDb0/cvuj6uVbDzVkPUJxF/fymgvmBcdY8MTIKz/usLhL7qvPra1h4RZWITWVxa6g=="
},
"@humanwhocodes/config-array": {
"version": "0.5.0",
@ -13611,9 +13611,9 @@
}
},
"rapidoc": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/rapidoc/-/rapidoc-9.1.2.tgz",
"integrity": "sha512-tW0ainfTZrCs3CklurkcCcoN0tIVilWPoy8ueUNQnt4oyu30vWjShq6+3Il3th3i/UQ1DAozHnMhWXOGKtkb4g==",
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/rapidoc/-/rapidoc-9.1.3.tgz",
"integrity": "sha512-9Mx/V/yO7hlM6mo5a6of0uJpm9rFBMFvqtr0Zw34JVIP19DN+cuYvUlB0MLrekn2sq0nvpzOW6cNkBIC1QSYYg==",
"requires": {
"@apitools/openapi-parser": "^0.0.9",
"base64-arraybuffer": "^1.0.1",
@ -13816,9 +13816,9 @@
}
},
"rollup": {
"version": "2.56.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz",
"integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==",
"version": "2.57.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.57.0.tgz",
"integrity": "sha512-bKQIh1rWKofRee6mv8SrF2HdP6pea5QkwBZSMImJysFj39gQuiV8MEPBjXOCpzk3wSYp63M2v2wkWBmFC8O/rg==",
"requires": {
"fsevents": "~2.3.2"
}

View File

@ -47,7 +47,7 @@
"@babel/preset-env": "^7.15.6",
"@babel/preset-typescript": "^7.15.0",
"@fortawesome/fontawesome-free": "^5.15.4",
"@goauthentik/api": "^2021.9.1-rc3-1632158454",
"@goauthentik/api": "^2021.9.2-1632578262",
"@lingui/cli": "^3.11.1",
"@lingui/core": "^3.11.1",
"@lingui/macro": "^3.11.1",
@ -82,8 +82,8 @@
"lit": "^2.0.0",
"moment": "^2.29.1",
"prettier": "^2.4.1",
"rapidoc": "^9.1.2",
"rollup": "^2.56.2",
"rapidoc": "^9.1.3",
"rollup": "^2.57.0",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",

View File

@ -1,7 +1,5 @@
import "@webcomponents/webcomponentsjs";
import "construct-style-sheets-polyfill";
import "lit/polyfill-support";
// @ts-ignore
window["polymerSkipLoadingFontRoboto"] = true;
import "construct-style-sheets-polyfill";
import "@webcomponents/webcomponentsjs";
import "lit/polyfill-support";

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 = "2021.9.1";
export const VERSION = "2021.9.3";
export const PAGE_SIZE = 20;
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -7,12 +7,11 @@ import { SpinnerButton } from "./SpinnerButton";
@customElement("ak-action-button")
export class ActionButton extends SpinnerButton {
@property({ attribute: false })
// eslint-disable-next-line @typescript-eslint/no-explicit-any
apiRequest: () => Promise<any> = () => {
apiRequest: () => Promise<unknown> = () => {
throw new Error();
};
callAction = (): Promise<void> => {
callAction = (): Promise<unknown> => {
this.setLoading();
return this.apiRequest().catch((e: Error | Response) => {
if (e instanceof Error) {

View File

@ -15,7 +15,7 @@ export class SpinnerButton extends LitElement {
isRunning = false;
@property()
callAction?: () => Promise<void>;
callAction?: () => Promise<unknown>;
static get styles(): CSSResult[] {
return [

View File

@ -1,9 +1,11 @@
import { html, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { CoreApi } from "@goauthentik/api";
import { DEFAULT_CONFIG } from "../../api/Config";
import { ERROR_CLASS, SECONDARY_CLASS, SUCCESS_CLASS } from "../../constants";
import { PFSize } from "../Spinner";
import { ActionButton } from "./ActionButton";
@customElement("ak-token-copy-button")
@ -27,21 +29,82 @@ export class TokenCopyButton extends ActionButton {
if (!token.key) {
return Promise.reject();
}
return navigator.clipboard.writeText(token.key).then(() => {
this.buttonClass = SUCCESS_CLASS;
setTimeout(() => {
this.buttonClass = SECONDARY_CLASS;
}, 1500);
});
setTimeout(() => {
this.buttonClass = SECONDARY_CLASS;
}, 1500);
this.buttonClass = SUCCESS_CLASS;
return token.key;
})
.catch((err: Response | undefined) => {
.catch((err: Error | Response | undefined) => {
this.buttonClass = ERROR_CLASS;
return err?.json().then((errResp) => {
throw new Error(errResp["detail"]);
if (err instanceof Error) {
setTimeout(() => {
this.buttonClass = SECONDARY_CLASS;
}, 1500);
throw err;
}
return err?.json().then((errResp) => {
setTimeout(() => {
this.buttonClass = SECONDARY_CLASS;
}, 1500);
throw new Error(errResp["detail"]);
});
});
};
render(): TemplateResult {
return html`<button
class="pf-c-button pf-m-progress ${this.classList.toString()}"
@click=${() => {
if (this.isRunning === true) {
return;
}
this.setLoading();
// Because safari is stupid, it only allows navigator.clipboard.write directly
// in the @click handler.
// And also chrome is stupid, because it doesn't accept Promises as values for
// ClipboardItem, so now there's two implementations
if (
navigator.userAgent.includes("Safari") &&
!navigator.userAgent.includes("Chrome")
) {
navigator.clipboard.write([
new ClipboardItem({
"text/plain": (this.callAction() as Promise<string>)
.then((key: string) => {
this.setDone(SUCCESS_CLASS);
return new Blob([key], {
type: "text/plain",
});
})
.catch((err: Error) => {
this.setDone(ERROR_CLASS);
throw err;
}),
}),
]);
} else {
(this.callAction() as Promise<string>)
.then((key: string) => {
navigator.clipboard.writeText(key).then(() => {
this.setDone(SUCCESS_CLASS);
});
})
.catch((err: Response | undefined) => {
return err?.json().then((errResp) => {
this.setDone(ERROR_CLASS);
throw new Error(errResp["detail"]);
});
});
}
}}
>
${this.isRunning
? html`<span class="pf-c-button__progress">
<ak-spinner size=${PFSize.Medium}></ak-spinner>
</span>`
: ""}
<slot></slot>
</button>`;
}
}

View File

@ -199,41 +199,36 @@ export class Form<T> extends LitElement {
);
return r;
})
.catch((ex: Response | Error) => {
.catch(async (ex: Response | Error) => {
if (ex instanceof Error) {
throw ex;
}
let msg = ex.statusText;
if (ex.status > 399 && ex.status < 500) {
return ex.json().then((errorMessage: ValidationError) => {
if (!errorMessage) return errorMessage;
if (errorMessage instanceof Error) {
throw errorMessage;
const errorMessage: ValidationError = await ex.json();
if (!errorMessage) return errorMessage;
if (errorMessage instanceof Error) {
throw errorMessage;
}
// assign all input-related errors to their elements
const elements: PaperInputElement[] = ironForm._getSubmittableElements();
elements.forEach((element) => {
const elementName = element.name;
if (!elementName) return;
if (camelToSnake(elementName) in errorMessage) {
element.errorMessage =
errorMessage[camelToSnake(elementName)].join(", ");
element.invalid = true;
}
// assign all input-related errors to their elements
const elements: PaperInputElement[] = ironForm._getSubmittableElements();
elements.forEach((element) => {
const elementName = element.name;
if (!elementName) return;
if (camelToSnake(elementName) in errorMessage) {
element.errorMessage =
errorMessage[camelToSnake(elementName)].join(", ");
element.invalid = true;
}
});
if ("non_field_errors" in errorMessage) {
this.nonFieldErrors = errorMessage["non_field_errors"];
}
throw new APIError(errorMessage);
});
}
throw ex;
})
.catch((ex: Error) => {
let msg = ex.toString();
// Only change the message when we have `detail`.
// Everything else is handled in the form.
if (ex instanceof APIError && "detail" in ex.response) {
msg = ex.response.detail;
if ("non_field_errors" in errorMessage) {
this.nonFieldErrors = errorMessage["non_field_errors"];
}
// Only change the message when we have `detail`.
// Everything else is handled in the form.
if ("detail" in errorMessage) {
msg = errorMessage.detail;
}
}
// error is local or not from rest_framework
showMessage({

View File

@ -15,7 +15,7 @@ import { EVENT_SIDEBAR_TOGGLE } from "../../constants";
import { first } from "../../utils";
// If the viewport is wider than MIN_WIDTH, the sidebar
// is shown besides the content, and not overlayed.
// is shown besides the content, and not overlaid.
export const MIN_WIDTH = 1200;
export const DefaultTenant: CurrentTenant = {

View File

@ -17,6 +17,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { CurrentTenant, EventsApi } from "@goauthentik/api";
import { DEFAULT_CONFIG, tenant } from "../api/Config";
import { configureSentry } from "../api/Sentry";
import { me } from "../api/Users";
import { WebsocketClient } from "../common/ws";
import {
@ -78,6 +79,7 @@ export class UserInterface extends LitElement {
}
.pf-c-brand {
min-height: 48px;
height: 48px;
}
.has-notifications {
color: #2b9af3;
@ -99,6 +101,7 @@ export class UserInterface extends LitElement {
this.firstUpdated();
});
tenant().then((tenant) => (this.tenant = tenant));
configureSentry(true);
}
firstUpdated(): void {

View File

@ -605,8 +605,8 @@ msgid "Certificate Fingerprint (SHA256)"
msgstr "Certificate Fingerprint (SHA256)"
#: src/pages/crypto/CertificateKeyPairListPage.ts
msgid "Certificate Subjet"
msgstr "Certificate Subjet"
msgid "Certificate Subject"
msgstr "Certificate Subject"
#: src/pages/providers/saml/SAMLProviderForm.ts
msgid "Certificate used to sign outgoing Responses going to the Service Provider."
@ -2331,8 +2331,8 @@ msgstr "Link to a user with identical email address. Can have security implicati
#: src/pages/sources/oauth/OAuthSourceForm.ts
#: src/pages/sources/plex/PlexSourceForm.ts
msgid "Link to a user with identical username address. Can have security implications when a username is used with another source."
msgstr "Link to a user with identical username address. Can have security implications when a username is used with another source."
msgid "Link to a user with identical username. Can have security implications when a username is used with another source."
msgstr "Link to a user with identical username. Can have security implications when a username is used with another source."
#: src/pages/stages/invitation/InvitationListLink.ts
msgid "Link to use the invitation."
@ -3659,8 +3659,8 @@ msgid "Select users to add"
msgstr "Select users to add"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
msgstr "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
msgid "Select which scopes can be used by the client. The client still has to specify the scope to access the data."
msgstr "Select which scopes can be used by the client. The client still has to specify the scope to access the data."
#: src/pages/sources/plex/PlexSourceForm.ts
msgid "Select which server a user has to be a member of to be allowed to authenticate."
@ -4552,6 +4552,14 @@ msgstr "Title"
msgid "To"
msgstr "To"
#: src/pages/users/UserViewPage.ts
msgid "To create a recovery link, the current tenant needs to have a recovery flow configured."
msgstr "To create a recovery link, the current tenant needs to have a recovery flow configured."
#: src/pages/users/UserListPage.ts
msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant."
msgstr "To directly reset a user's password, configure a recovery flow on the currently active tenant."
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "To use SSL instead, use 'ldaps://' and disable this option."
msgstr "To use SSL instead, use 'ldaps://' and disable this option."

View File

@ -601,7 +601,7 @@ msgid "Certificate Fingerprint (SHA256)"
msgstr ""
#: src/pages/crypto/CertificateKeyPairListPage.ts
msgid "Certificate Subjet"
msgid "Certificate Subject"
msgstr ""
#: src/pages/providers/saml/SAMLProviderForm.ts
@ -2323,7 +2323,7 @@ msgstr ""
#: src/pages/sources/oauth/OAuthSourceForm.ts
#: src/pages/sources/plex/PlexSourceForm.ts
msgid "Link to a user with identical username address. Can have security implications when a username is used with another source."
msgid "Link to a user with identical username. Can have security implications when a username is used with another source."
msgstr ""
#: src/pages/stages/invitation/InvitationListLink.ts
@ -3651,7 +3651,7 @@ msgid "Select users to add"
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Select which scopes can be used by the client. The client stil has to specify the scope to access the data."
msgid "Select which scopes can be used by the client. The client still has to specify the scope to access the data."
msgstr ""
#: src/pages/sources/plex/PlexSourceForm.ts
@ -4537,6 +4537,14 @@ msgstr ""
msgid "To"
msgstr ""
#: src/pages/users/UserViewPage.ts
msgid "To create a recovery link, the current tenant needs to have a recovery flow configured."
msgstr ""
#: src/pages/users/UserListPage.ts
msgid "To directly reset a user's password, configure a recovery flow on the currently active tenant."
msgstr ""
#: src/pages/sources/ldap/LDAPSourceForm.ts
msgid "To use SSL instead, use 'ldaps://' and disable this option."
msgstr ""

View File

@ -137,7 +137,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text"
>${t`Certificate Subjet`}</span
>${t`Certificate Subject`}</span
>
</dt>
<dd class="pf-c-description-list__description">

View File

@ -20,9 +20,14 @@ import { first } from "../../utils";
@customElement("ak-event-transport-form")
export class TransportForm extends ModelForm<NotificationTransport, string> {
loadInstance(pk: string): Promise<NotificationTransport> {
return new EventsApi(DEFAULT_CONFIG).eventsTransportsRetrieve({
uuid: pk,
});
return new EventsApi(DEFAULT_CONFIG)
.eventsTransportsRetrieve({
uuid: pk,
})
.then((transport) => {
this.onModeChange(transport.mode);
return transport;
});
}
@property({ type: Boolean })
@ -72,12 +77,6 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
`;
}
firstUpdated(): void {
if (this.instance) {
this.onModeChange(this.instance.mode);
}
}
onModeChange(mode: string): void {
if (
mode === NotificationTransportModeEnum.Webhook ||
@ -114,6 +113,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
?hidden=${!this.showWebhook}
label=${t`Webhook URL`}
name="webhookUrl"
?required=${true}
>
<input
type="text"

View File

@ -104,7 +104,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
})
.then((flows) => {
return flows.results.map((flow) => {
// No ?selected check here, as this input isnt shown on update forms
// No ?selected check here, as this input isn't shown on update forms
return html`<option value=${ifDefined(flow.pk)}>
${flow.name} (${flow.slug})
</option>`;

View File

@ -42,6 +42,7 @@ export class OutpostDeploymentModal extends ModalButton {
</label>
<div>
<ak-token-copy-button
class="pf-m-primary"
identifier="${ifDefined(this.outpost?.tokenIdentifier)}"
>
${t`Click to copy token`}

View File

@ -14,7 +14,7 @@ import { ModelForm } from "../../elements/forms/ModelForm";
@customElement("ak-property-mapping-notification-form")
export class PropertyMappingNotification extends ModelForm<NotificationWebhookMapping, string> {
loadInstance(pk: string): Promise<NotificationWebhookMapping> {
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsNotificationRetrieveRaw({
return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsNotificationRetrieve({
pmUuid: pk,
});
}

View File

@ -277,7 +277,7 @@ ${this.instance?.redirectUris}</textarea
)}
</select>
<p class="pf-c-form__helper-text">
${t`Select which scopes can be used by the client. The client stil has to specify the scope to access the data.`}
${t`Select which scopes can be used by the client. The client still has to specify the scope to access the data.`}
</p>
<p class="pf-c-form__helper-text">
${t`Hold control/command to select multiple items.`}

View File

@ -219,7 +219,7 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
?selected=${this.instance?.userMatchingMode ===
UserMatchingModeEnum.UsernameLink}
>
${t`Link to a user with identical username address. Can have security implications when a username is used with another source.`}
${t`Link to a user with identical username. Can have security implications when a username is used with another source.`}
</option>
<option
value=${UserMatchingModeEnum.UsernameDeny}

View File

@ -204,7 +204,7 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
?selected=${this.instance?.userMatchingMode ===
UserMatchingModeEnum.UsernameLink}
>
${t`Link to a user with identical username address. Can have security implications when a username is used with another source.`}
${t`Link to a user with identical username. Can have security implications when a username is used with another source.`}
</option>
<option
value=${UserMatchingModeEnum.UsernameDeny}

View File

@ -74,7 +74,7 @@ export class UserWriteStageForm extends ModelForm<UserWriteStage, string> {
${t`Mark newly created users as inactive.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${t`Group`} name="searchGroup">
<ak-form-element-horizontal label=${t`Group`} name="createUsersGroup">
<select class="pf-c-form-control">
<option
value=""

View File

@ -171,72 +171,78 @@ export class UserListPage extends TablePage<User> {
</div>
</dd>
</div>
${until(
tenant().then((te) => {
if (!te.flowRecovery) {
return html``;
}
return html`<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text"
>${t`Recovery`}</span
>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
<ak-action-button
.apiRequest=${() => {
return new CoreApi(DEFAULT_CONFIG)
.coreUsersRecoveryRetrieve({
id: item.pk || 0,
})
.then((rec) => {
showMessage({
level: MessageLevel.success,
message: t`Successfully generated recovery link`,
description: rec.link,
});
})
.catch((ex: Response) => {
ex.json().then(() => {
<div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${t`Recovery`}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">
${until(
tenant().then((tenant) => {
if (!tenant.flowRecovery) {
return html`
<p>
${t`To directly reset a user's password, configure a recovery flow on the currently active tenant.`}
</p>
`;
}
return html`
<ak-action-button
.apiRequest=${() => {
return new CoreApi(DEFAULT_CONFIG)
.coreUsersRecoveryRetrieve({
id: item.pk || 0,
})
.then((rec) => {
showMessage({
level: MessageLevel.error,
message: t`No recovery flow is configured.`,
level: MessageLevel.success,
message: t`Successfully generated recovery link`,
description: rec.link,
});
})
.catch((ex: Response) => {
ex.json().then(() => {
showMessage({
level: MessageLevel.error,
message: t`No recovery flow is configured.`,
});
});
});
});
}}
>
${t`Copy recovery link`}
</ak-action-button>
${item.email
? html`<ak-forms-modal
.closeAfterSuccessfulSubmit=${false}
>
<span slot="submit"> ${t`Send link`} </span>
<span slot="header">
${t`Send recovery link to user`}
</span>
<ak-user-reset-email-form
slot="form"
.user=${item}
}}
>
${t`Copy recovery link`}
</ak-action-button>
${item.email
? html`<ak-forms-modal
.closeAfterSuccessfulSubmit=${false}
>
</ak-user-reset-email-form>
<button
slot="trigger"
class="pf-c-button pf-m-secondary"
>
${t`Email recovery link`}
</button>
</ak-forms-modal>`
: html`<span
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
>`}
</div>
</dd>
</div>`;
}),
)}
<span slot="submit">
${t`Send link`}
</span>
<span slot="header">
${t`Send recovery link to user`}
</span>
<ak-user-reset-email-form
slot="form"
.user=${item}
>
</ak-user-reset-email-form>
<button
slot="trigger"
class="pf-c-button pf-m-secondary"
>
${t`Email recovery link`}
</button>
</ak-forms-modal>`
: html`<span
>${t`Recovery link cannot be emailed, user has no email address saved.`}</span
>`}
`;
}),
)}
</div>
</dd>
</div>
</dl>
</div>
</td>

View File

@ -197,6 +197,7 @@ export class UserViewPage extends LitElement {
</div>
<div class="pf-c-card__footer">
<ak-action-button
class="pf-m-secondary"
.apiRequest=${() => {
return new CoreApi(DEFAULT_CONFIG)
.coreUsersRecoveryRetrieve({
@ -208,6 +209,13 @@ export class UserViewPage extends LitElement {
message: t`Successfully generated recovery link`,
description: rec.link,
});
})
.catch(() => {
showMessage({
level: MessageLevel.error,
message: t`To create a recovery link, the current tenant needs to have a recovery flow configured.`,
description: "",
});
});
}}
>

View File

@ -29,6 +29,9 @@ export class LibraryPage extends LitElement {
@property({ attribute: false })
selectedApp?: Application;
@property()
query?: string;
fuse?: Fuse<Application>;
constructor() {
@ -38,6 +41,10 @@ export class LibraryPage extends LitElement {
this.fuse = new Fuse(apps.results, {
keys: ["slug", "name"],
});
if (!this.fuse || !this.query) return;
const matchingApps = this.fuse.search(this.query);
if (matchingApps.length < 1) return;
this.selectedApp = matchingApps[0].item;
});
}
@ -116,9 +123,9 @@ export class LibraryPage extends LitElement {
${config.enabledFeatures.search
? html`<input
@input=${(ev: InputEvent) => {
const query = (ev.target as HTMLInputElement).value;
this.query = (ev.target as HTMLInputElement).value;
if (!this.fuse) return;
const apps = this.fuse.search(query);
const apps = this.fuse.search(this.query);
if (apps.length < 1) return;
this.selectedApp = apps[0].item;
}}

View File

@ -25,7 +25,7 @@ log_level: debug
secret_key: "A long key you can generate with `pwgen 40 1` for example"
```
Afterwards, you can start authentik by running `make run`.
Afterwards, you can start authentik by running `make run`. authentik is now accessible under `localhost:9000`.
Generally speaking, authentik is a Django application, ran by gunicorn, proxied by a Go application. The Go application serves static files.
@ -37,15 +37,7 @@ Run `make gen` to generate an updated OpenAPI document for any changes you made.
## Frontend
By default, no transpiled bundle of the frontend is included. To build the UI, you need Node 12 or newer.
The Frontend also uses a generated API client to talk with the backend. To generate this client, [openapitools/openapi-generator-cli](https://github.com/OpenAPITools/openapi-generator) is used.
If you want to generate the client without installing anything, run this command:
```shell
make gen-web
```
By default, no compiled bundle of the frontend is included. To build the UI, you need Node 14 or newer.
To build the UI, run these commands:

View File

@ -6,6 +6,10 @@ title: Example Flows
You can apply theses flows multiple times to stay updated, however this will discard all changes you've made.
:::
:::info
The example flows provided below will **override** the default flows, please review the contents of the example flow before importing and consider exporting the affected existing flows first.
:::
## Enrollment (2 Stage)
Flow: right-click [here](/flows/enrollment-2-stage.akflow) and save the file.

View File

@ -48,14 +48,14 @@ To use an S3-compatible storage, set the following settings.
- `AUTHENTIK_REDIS__OUTPOST_SESSION_DB`: Database for sessions for the embedded outpost, defaults to 3
- `AUTHENTIK_REDIS__CACHE_TIMEOUT`: Timeout for cached data until it expires in seconds, defaults to 300
- `AUTHENTIK_REDIS__CACHE_TIMEOUT_FLOWS`: Timeout for cached flow plans until they expire in seconds, defaults to 300
- `AUTHENTIK_REDIS__CACHE_TIMEOUT_POLICIES`: Timeout for cached polices until they expire in seconds, defaults to 300
- `AUTHENTIK_REDIS__CACHE_TIMEOUT_POLICIES`: Timeout for cached policies until they expire in seconds, defaults to 300
- `AUTHENTIK_REDIS__CACHE_TIMEOUT_REPUTATION`: Timeout for cached reputation until they expire in seconds, defaults to 300
## authentik Settings
### AUTHENTIK_SECRET_KEY
Secret key used for cookie singing and unique user IDs, don't change this after the first install.
Secret key used for cookie signing and unique user IDs, don't change this after the first install.
### AUTHENTIK_LOG_LEVEL

View File

@ -12,9 +12,9 @@ This installation method is for test-setups and small-scale productive setups.
## Preparation
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.9.1/docker-compose.yml). Place it in a directory of your choice.
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.9.3/docker-compose.yml). Place it in a directory of your choice.
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.9.1 >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.9.3 >> .env`
If this is a fresh authentik install run the following commands to generate a password:
@ -31,7 +31,7 @@ echo "AUTHENTIK_ERROR_REPORTING__ENABLED=true" >> .env
## Email configuration (optional, but recommended)
It is also recommended to configure global email credentials. These are used by authentik to notify you about alerts, configuration issues. They can also be used by [Email stages](flow/stages/email/index.md) to send verification/recovery emails.
It is also recommended to configure global email credentials. These are used by authentik to notify you about alerts and configuration issues. They can also be used by [Email stages](flow/stages/email/index.md) to send verification/recovery emails.
Append this block to your `.env` file

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