Compare commits

..

61 Commits

Author SHA1 Message Date
276d8fe5cf release: 2021.8.4 2021-09-02 20:21:21 +02:00
92ce5f0931 web: improve error display when only {'detail'} is returned
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 19:55:37 +02:00
7fea20375f *: fix tests not using APITestCase
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 19:14:21 +02:00
d4d4034d2c web: Update Web API Client version (#1336)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-02 17:42:55 +02:00
f0db408699 api: add v3
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 17:40:02 +02:00
5e200655d9 web: Update Web API Client version (#1335)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-02 17:13:16 +02:00
d5d1f2a645 web: show version in logs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 17:10:43 +02:00
cc5cc43baa api: fix sentry endpoint not working due to mime-media
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 16:56:53 +02:00
e512f085db root: allow enabling s3 backup ssl verification
closes #1332

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 09:41:55 +02:00
f323c01bd8 build(deps): bump django from 3.2.6 to 3.2.7 (#1333) 2021-09-02 09:12:24 +02:00
f56cacb406 build(deps): bump boto3 from 1.18.33 to 1.18.34 (#1334) 2021-09-02 09:12:03 +02:00
eaecd31e9f ci: always run codecov and testspace
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 22:59:51 +02:00
36989d82e1 ci: merge on testspace
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 22:35:17 +02:00
50777d9022 ci: re-add testspace (#1331)
* ci: re-add testspace

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

* ci: fix double k3d

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 22:33:10 +02:00
a15571bd3e outposts/proxy: detect empty authentik_host
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 22:09:07 +02:00
26fd66d831 stages/authenticator_validate: fix variable shadowing, optimization
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 19:54:54 +02:00
0be873025a ci: fix bumpversion path
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 19:38:04 +02:00
28ada49910 website/docs: final 2021.8.4 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 19:37:00 +02:00
4fc8e61f8c stages/authenticator_validate: show single button for multiple webauthn authenticators
tested with browser + yubikey 5

closes #1096

The order of allowCredentials doesn't seem to matter, chrome seems to always choose the internal authenticator first.

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 19:28:52 +02:00
7d26ea1a9c web/admin: fix list of webauthn devices not updating after rename
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 19:05:18 +02:00
3a58dc62e1 ci: fix missing branch
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 18:34:57 +02:00
71fe7bc827 ci: fix sha being used instead of timestamp
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 17:10:42 +02:00
933336c38b ci: fix images not being pushed with correct tags
* ci: debug

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

* ci: fix branch and sha

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 16:19:29 +02:00
371feb9a31 ci: fix images not being pushed
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 15:07:13 +02:00
95a2fd3c9e web: Update Web API Client version (#1327)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-09-01 14:48:48 +02:00
17cb76c334 stages/invitation: fix invitation not inheriting ExpiringModel
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 14:25:19 +02:00
88f0dfc8cc web/admin: fallback for invitation list on first load
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 13:33:05 +02:00
f82aada23b web/admin: fix flow executor not opening in new tab
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-01 13:19:09 +02:00
ecaee92634 build(deps): bump @sentry/tracing from 6.11.0 to 6.12.0 in /web (#1322)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.11.0...6.12.0)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-01 09:26:53 +02:00
89252ec47b build(deps): bump @sentry/tracing from 6.11.0 to 6.12.0 in /website (#1320)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.11.0...6.12.0)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-01 09:26:33 +02:00
f0f25ab291 build(deps): bump @sentry/react from 6.11.0 to 6.12.0 in /website (#1321) 2021-09-01 08:40:07 +02:00
e4d0fec15a build(deps): bump @sentry/browser from 6.11.0 to 6.12.0 in /web (#1323) 2021-09-01 08:39:56 +02:00
6b10baf086 build(deps): bump docker from 5.0.0 to 5.0.1 (#1324) 2021-09-01 08:39:21 +02:00
f148b5d341 build(deps): bump boto3 from 1.18.32 to 1.18.33 (#1326) 2021-09-01 08:39:12 +02:00
1471ff8940 build(deps): bump drf-spectacular from 0.18.0 to 0.18.1 (#1325) 2021-09-01 08:39:01 +02:00
d9a6ec2ac0 webiste/docs: update extensionvs/v1beta ingress
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-31 21:11:01 +02:00
5745ffa0a8 ci: don't login to docker on forks
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-31 09:31:10 +02:00
b26202db35 build(deps): bump @typescript-eslint/parser in /web (#1316) 2021-08-31 08:42:14 +02:00
6318577a51 build(deps): bump @typescript-eslint/eslint-plugin in /web (#1317) 2021-08-31 08:16:59 +02:00
6a2cd45847 build(deps-dev): bump pytest from 6.2.4 to 6.2.5 (#1318) 2021-08-31 08:16:44 +02:00
ef5cea2c01 build(deps): bump boto3 from 1.18.31 to 1.18.32 (#1319) 2021-08-31 08:16:32 +02:00
69f4d54bae ci: migrate ci to gh actions (#1315) 2021-08-30 20:21:15 +02:00
b1eec5a7d2 outposts/proxy: add more logging
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-30 17:18:52 +02:00
1b8271d767 flows: disable compatibility_mode by default
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-30 17:18:43 +02:00
3e9f5ec5ef providers/proxy: improve error handling for non-tls ingresses
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-30 14:43:57 +02:00
63f57b6a77 events: improve logging for task exceptions
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-30 14:43:44 +02:00
a016f99450 core: fix user_obj being empty on token API
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-30 12:51:17 +02:00
adc18b2991 build(deps): bump boto3 from 1.18.30 to 1.18.31 (#1314)
Bumps [boto3](https://github.com/boto/boto3) from 1.18.30 to 1.18.31.
- [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.30...1.18.31)

---
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-08-30 09:12:58 +02:00
e37a326b95 website/docs: prepare 8.4 docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 22:12:49 +02:00
048467e97d outpost/ldap: delay user information removal upon closing of connection
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 21:13:46 +02:00
cc2cd6919f outpost/embedded: only send requests for non-akprox paths when we're doing proxy mode
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 21:13:28 +02:00
0c6e781e5b providers/proxy: fix traefik middleware being generated with wrong ports for embedded outposts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 20:49:11 +02:00
7294d8fca5 website/docs: add note for cross-namespace reference in traefik
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 20:46:17 +02:00
16ec5680b4 web: Update Web API Client version (#1313)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2021-08-29 19:51:10 +02:00
87920fb1d7 website/docs: add docs for websocket connections
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 19:49:18 +02:00
523b96a6d2 api: add basic rate limiting for sentry endpoint
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 19:33:18 +02:00
45731d8069 cmd: add option to disable embedded outpost
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 19:19:13 +02:00
e872371970 website/docs: add embedded outpost docs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 14:43:13 +02:00
08e8cf850a web/flows: fix FlowExecutor not updating when challenge changes from outside
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 13:49:57 +02:00
b1ed2154ac policies/password: fix PasswordStage not being usable with prompt stages, rework validation logic
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-29 00:40:36 +02:00
7ef2aa3eb9 web: Update Web API Client version (#1312) 2021-08-28 19:08:38 +02:00
105 changed files with 1702 additions and 3207 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.8.3 current_version = 2021.8.4
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*) parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
@ -23,7 +23,7 @@ values =
[bumpversion:file:schema.yml] [bumpversion:file:schema.yml]
[bumpversion:file:.github/workflows/release.yml] [bumpversion:file:.github/workflows/release-publish.yml]
[bumpversion:file:authentik/__init__.py] [bumpversion:file:authentik/__init__.py]

240
.github/workflows/ci-main.yml vendored Normal file
View File

@ -0,0 +1,240 @@
name: authentik-ci-main
on:
push:
paths-ignore:
- website
env:
POSTGRES_DB: authentik
POSTGRES_USER: authentik
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
jobs:
lint-pylint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- name: run pylint
run: pipenv run pylint authentik tests lifecycle
lint-black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- name: run black
run: pipenv run black --check authentik tests lifecycle
lint-isort:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- name: run isort
run: pipenv run isort --check authentik tests lifecycle
lint-bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- name: run bandit
run: pipenv run bandit -r authentik tests lifecycle
lint-pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: prepare
run: |
scripts/ci_prepare.sh
npm install -g pyright@1.1.136
- name: run bandit
run: pipenv run pyright e2e lifecycle
test-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- name: run migrations
run: pipenv run python -m lifecycle.migrate
test-migrations-from-stable:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: checkout stable
run: |
# Copy current, latest config to local
cp authentik/lib/default.yml local.env.yml
git checkout $(git describe --abbrev=0 --match 'version/*')
- name: prepare
run: scripts/ci_prepare.sh
- name: run migrations to stable
run: pipenv run python -m lifecycle.migrate
- name: checkout current code
run: |
set -x
git checkout $GITHUB_REF
pipenv sync --dev
- name: migrate to latest
run: pipenv run python -m lifecycle.migrate
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- name: run unittest
run: |
pipenv run make test
pipenv run coverage xml
- name: run testspace
if: ${{ always() }}
run: |
testspace unittest.xml ?add
- if: ${{ always() }}
uses: codecov/codecov-action@v2
test-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: prepare
run: scripts/ci_prepare.sh
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- name: prepare k3d
run: |
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash
- name: run integration
run: |
pipenv run make test-integration
pipenv run coverage xml
- name: run testspace
if: ${{ always() }}
run: |
testspace unittest.xml ?add
- if: ${{ always() }}
uses: codecov/codecov-action@v2
test-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- name: prepare
run: |
scripts/ci_prepare.sh
docker-compose -f tests/e2e/ci.docker-compose.yml up -d
- name: prepare web ui
run: |
cd web
npm i
npm run build
- name: run e2e
run: |
pipenv run make test-e2e
pipenv run coverage xml
- name: run testspace
if: ${{ always() }}
run: |
testspace unittest.xml ?add
- if: ${{ always() }}
uses: codecov/codecov-action@v2
report:
if: ${{ always() }}
needs:
- test-unittest
- test-integration
- test-e2e
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- name: finish testspace
run: |
testspace ?finish
build:
needs:
- lint-pylint
- lint-black
- lint-isort
- lint-bandit
- lint-pyright
- test-migrations
- test-migrations-from-stable
- test-unittest
- test-integration
- test-e2e
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: prepare variables
id: ev
run: |
python ./scripts/gh_do_set_branch.py
- name: Login to Container Registry
uses: docker/login-action@v1
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
with:
registry: beryju.org
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
tags: |
beryju.org/authentik/server:gh-${{ steps.ev.outputs.branchName }}
beryju.org/authentik/server:gh-${{ steps.ev.outputs.branchName }}-${{ steps.ev.outputs.timestamp }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}

66
.github/workflows/ci-outpost.yml vendored Normal file
View File

@ -0,0 +1,66 @@
name: authentik-ci-outpost
on:
push:
jobs:
lint-golint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.16.3'
- name: Generate API
run: |
make gen-outpost
- name: Run linter
run: |
# Create folder structure for go embeds
mkdir -p web/dist
mkdir -p website/help
touch web/dist/test website/help/test
docker run \
--rm \
-v $(pwd):/app \
-w /app \
golangci/golangci-lint:v1.39.0 \
golangci-lint run -v --timeout 200s
build:
needs:
- lint-golint
strategy:
matrix:
type:
- proxy
- ldap
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: prepare variables
id: ev
run: |
python ./scripts/gh_do_set_branch.py
- name: Login to Container Registry
uses: docker/login-action@v1
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
with:
registry: beryju.org
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}
- name: Building Docker Image
uses: docker/build-push-action@v2
with:
push: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
tags: |
beryju.org/authentik/outpost-${{ matrix.type }}:gh-${{ steps.ev.outputs.branchName }}
beryju.org/authentik/outpost-${{ matrix.type }}:gh-${{ steps.ev.outputs.branchName }}-${{ steps.ev.outputs.timestamp }}
beryju.org/authentik/outpost-${{ matrix.type }}:gh-${{ steps.ev.outputs.sha }}
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}

82
.github/workflows/ci-web.yml vendored Normal file
View File

@ -0,0 +1,82 @@
name: authentik-ci-web
on:
push:
jobs:
lint-eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: |
cd web
npm install
- name: Generate API
run: make gen-web
- name: Eslint
run: |
cd web
npm run lint
lint-prettier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: |
cd web
npm install
- name: Generate API
run: make gen-web
- name: prettier
run: |
cd web
npm run prettier-check
lint-lit-analyse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: |
cd web
npm install
- name: Generate API
run: make gen-web
- name: prettier
run: |
cd web
npm run lit-analyse
build:
needs:
- lint-eslint
- lint-prettier
- lint-lit-analyse
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- run: |
cd web
npm install
- name: Generate API
run: make gen-web
- name: build
run: |
cd web
npm run build

View File

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

156
Pipfile.lock generated
View File

@ -122,19 +122,19 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:542336dda9a728c250cf24aea6d87454136d9d6f3d8a84ec5a737a7edba3b932", "sha256:5116e9bdec19adcc5531a9b7b535be77d5314eef092aaf7033ace48a9be65036",
"sha256:9bf2a281a6df9f8948d3d322d532d03a1039f57a049a1aa2b72b4a28c9627013" "sha256:658ddf4ba552f654fd4d48335fa95ff4e3e1a4e82f90021a1a1d3de4a5428ba4"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.18.30" "version": "==1.18.34"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:26ab09126dd05c968fbbcb894a1d623355e6119ff6d4a2bf5d292e3ad7cdd628", "sha256:1b4999fb0e1a4c050c4d9118ebdaac8d83761ef32c3c0f13a25f9204045998fe",
"sha256:9b0b3dbc144178e2b803097abcc95712a03b8dde5a02e4335ac870bc6c129dd9" "sha256:ec2cdf1c8ed64a7f392f352125d248c76103fa9d137b275b7c76836776cedf56"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==1.21.30" "version": "==1.21.34"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -359,11 +359,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13", "sha256:95b318319d6997bac3595517101ad9cc83fe5672ac498ba48d1a410f47afecd2",
"sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022" "sha256:e93c93565005b37ddebf2396b4dc4b6913c1838baa82efdfb79acedd5816c240"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.2.6" "version": "==3.2.7"
}, },
"django-dbbackup": { "django-dbbackup": {
"git": "https://github.com/django-dbbackup/django-dbbackup.git", "git": "https://github.com/django-dbbackup/django-dbbackup.git",
@ -443,19 +443,19 @@
}, },
"docker": { "docker": {
"hashes": [ "hashes": [
"sha256:3e8bc47534e0ca9331d72c32f2881bb13b93ded0bcdeab3c833fb7cf61c0a9a5", "sha256:5aafaec0d2a1de0e32010b43b5eac9f6f851c9db99a46ad32b8e44eeeb55616d",
"sha256:fc961d622160e8021c10d1bcabc388c57d55fb1f917175afbe24af442e6879bd" "sha256:b88eef725b33c0ed59c67506631bbb09b480b7ca5a739bbbb948b446443fe914"
], ],
"index": "pypi", "index": "pypi",
"version": "==5.0.0" "version": "==5.0.1"
}, },
"drf-spectacular": { "drf-spectacular": {
"hashes": [ "hashes": [
"sha256:5b1c27de127c86564be5a967a6fa195cfe161b552d98364282ae9e6ed3d75a85", "sha256:98681add6671db9e6dba5f0d3dcf8aab5950cbb978497390507356e593bf082f",
"sha256:8588706c27f44adfbb3405bae9ef9cd6506f4b59d4cbd66c59780dce035602d9" "sha256:a430bab0f4ecfc90786b7b63bbee3f9a56094201fbed9bdfbf952e99e6469104"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.18.0" "version": "==0.18.1"
}, },
"duo-client": { "duo-client": {
"hashes": [ "hashes": [
@ -490,11 +490,11 @@
}, },
"google-auth": { "google-auth": {
"hashes": [ "hashes": [
"sha256:c012c8be7c442c8309ca8fa0876fef33f5fd977c467be1e1c1c2f721e8ebd73c", "sha256:104475dc4d57bbae49017aea16fffbb763204fa2d6a70f1f3cc79962c1a383a4",
"sha256:ea1af050b3e06eb73e4470f704d23007307bc0e87c13e015f6b90460f1407bd3" "sha256:cde472372e030e1e0bc64dac00fb53e6c095d7ab641f4281e2c995e85e205d8b"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.0.1" "version": "==2.0.2"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@ -1151,11 +1151,11 @@
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"
], ],
"version": "==3.10.0.0" "version": "==3.10.0.2"
}, },
"ua-parser": { "ua-parser": {
"hashes": [ "hashes": [
@ -1420,11 +1420,11 @@
}, },
"astroid": { "astroid": {
"hashes": [ "hashes": [
"sha256:b6c2d75cd7c2982d09e7d41d70213e863b3ba34d3bd4014e08f167cee966e99e", "sha256:3b680ce0419b8a771aba6190139a3998d14b413852506d99aff8dc2bf65ee67c",
"sha256:ecc50f9b3803ebf8ea19aa2c6df5622d8a5c31456a53c741d3be044d96ff0948" "sha256:dc1e8b28427d6bbef6b8842b18765ab58f558c42bb80540bd7648c98412af25e"
], ],
"markers": "python_version ~= '3.6'", "markers": "python_version ~= '3.6'",
"version": "==2.7.2" "version": "==2.7.3"
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
@ -1582,7 +1582,7 @@
"sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899", "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899",
"sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2" "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"
], ],
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'", "markers": "python_version < '4' and python_full_version >= '3.6.1'",
"version": "==5.9.3" "version": "==5.9.3"
}, },
"lazy-object-proxy": { "lazy-object-proxy": {
@ -1652,19 +1652,19 @@
}, },
"platformdirs": { "platformdirs": {
"hashes": [ "hashes": [
"sha256:4666d822218db6a262bdfdc9c39d21f23b4cfdb08af331a81e92751daf6c866c", "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f",
"sha256:632daad3ab546bd8e6af0537d09805cec458dce201bccfe23012df73332e181e" "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.2.0" "version": "==2.3.0"
}, },
"pluggy": { "pluggy": {
"hashes": [ "hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==0.13.1" "version": "==1.0.0"
}, },
"py": { "py": {
"hashes": [ "hashes": [
@ -1707,11 +1707,11 @@
}, },
"pytest": { "pytest": {
"hashes": [ "hashes": [
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b", "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89",
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890" "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.2.4" "version": "==6.2.5"
}, },
"pytest-django": { "pytest-django": {
"hashes": [ "hashes": [
@ -1758,49 +1758,49 @@
}, },
"regex": { "regex": {
"hashes": [ "hashes": [
"sha256:0696eb934dee723e3292056a2c046ddb1e4dd3887685783a9f4af638e85dee76", "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468",
"sha256:105122fa63da98d8456d5026bc6ac5a1399fd82fa6bad22c6ea641b1572c9142", "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354",
"sha256:116c277774f84266044e889501fe79cfd293a8b4336b7a5e89b9f20f1e5a9f21", "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308",
"sha256:12eaf0bbe568bd62e6cade7937e0bf01a2a4cef49a82f4fd204401e78409e158", "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d",
"sha256:1401cfa4320691cbd91191ec678735c727dee674d0997b0902a5a38ad482faf5", "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc",
"sha256:19acdb8831a4e3b03b23369db43178d8fee1f17b99c83af6cd907886f76bd9d4", "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8",
"sha256:208851a2f8dd31e468f0b5aa6c94433975bd67a107a4e7da3bdda947c9f85e25", "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797",
"sha256:24d68499a27b2d93831fde4a9b84ea5b19e0ab141425fbc9ab1e5b4dad179df7", "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2",
"sha256:2778c6cb379d804e429cc8e627392909e60db5152b42c695c37ae5757aae50ae", "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13",
"sha256:2a0a5e323cf86760784ce2b91d8ab5ea09d0865d6ef4da0151e03d15d097b24e", "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d",
"sha256:2d9cbe0c755ab8b6f583169c0783f7278fc6b195e423b09c5a8da6f858025e96", "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a",
"sha256:2de1429e4eeab799c168a4f6e6eecdf30fcaa389bba4039cc8a065d6b7aad647", "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0",
"sha256:32753eda8d413ce4f208cfe01dd61171a78068a6f5d5f38ccd751e00585cdf1d", "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73",
"sha256:3ee8ad16a35c45a5bab098e39020ecb6fec3b0e700a9d88983d35cbabcee79c8", "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1",
"sha256:4f03fc0a25122cdcbf39136510d4ea7627f732206892db522adf510bc03b8c67", "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed",
"sha256:4f3e36086d6631ceaf468503f96a3be0d247caef0660c9452fb1b0c055783851", "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a",
"sha256:503c1ba0920a46a1844363725215ef44d59fcac2bd2c03ae3c59aa9d08d29bd6", "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b",
"sha256:507861cf3d97a86fbe26ea6cc04660ae028b9e4080b8290e28b99547b4e15d89", "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f",
"sha256:56ae6e3cf0506ec0c40b466e31f41ee7a7149a2b505ae0ee50edd9043b423d27", "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256",
"sha256:6530b7b9505123cdea40a2301225183ca65f389bc6129f0c225b9b41680268d8", "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb",
"sha256:6729914dd73483cd1c8aaace3ac082436fc98b0072743ac136eaea0b3811d42f", "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2",
"sha256:7406dd2e44c7cfb4680c0a45a03264381802c67890cf506c147288f04c67177d", "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983",
"sha256:7684016b73938ca12d160d2907d141f06b7597bd17d854e32bb7588be01afa1d", "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb",
"sha256:7db58ad61f3f6ea393aaf124d774ee0c58806320bc85c06dc9480f5c7219c250", "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645",
"sha256:83946ca9278b304728b637bc8d8200ab1663a79de85e47724594917aeed0e892", "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8",
"sha256:84057cfae5676f456b03970eb78b7e182fddc80c2daafd83465a3d6ca9ff8dbf", "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a",
"sha256:862b6164e9a38b5c495be2c2854e75fd8af12c5be4c61dc9b42d255980d7e907", "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906",
"sha256:8ddb4f9ce6bb388ecc97b4b3eb37e786f05d7d5815e8822e0d87a3dbd7100649", "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f",
"sha256:92eb03f47427fea452ff6956d11f5d5a3f22a048c90a0f34fa223e6badab6c85", "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c",
"sha256:a5f3bc727fea58f21d99c22e6d4fca652dc11dbc2a1e7cfc4838cd53b2e3691f", "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892",
"sha256:a6180dbf5945b27e9420e1b58c3cacfc79ad5278bdad3ea35109f5680fbe16d1", "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0",
"sha256:b158f673ae6a6523f13704f70aa7e4ce875f91e379bece4362c89db18db189d5", "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e",
"sha256:cd45b4542134de63e7b9dd653e0a2d7d47ffed9615e3637c27ca5f6b78ea68bb", "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e",
"sha256:d2404336fd16788ea757d4218a2580de60adb052d9888031e765320be8884309", "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed",
"sha256:db888d4fb33a2fd54b57ac55d5015e51fa849f0d8592bd799b4e47f83bd04e00", "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c",
"sha256:dde0ac721c7c5bfa5f9fc285e811274dec3c392f2c1225f7d07ca98a8187ca84", "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374",
"sha256:de0d06ccbc06af5bf93bddec10f4f80275c5d74ea6d28b456931f3955f58bc8c", "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd",
"sha256:e02dad60e3e8442eefd28095e99b2ac98f2b8667167493ac6a2f3aadb5d84a17", "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791",
"sha256:e960fe211496333b2f7e36badf4c22a919d740386681f79139ee346b403d1ca1", "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a",
"sha256:e9700c52749cb3e90c98efd72b730c97b7e4962992fca5fbcaf1363be8e3b849", "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1",
"sha256:ee318974a1fdacba1701bc9e552e9015788d6345416364af6fa987424ff8df53" "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"
], ],
"version": "==2021.8.27" "version": "==2021.8.28"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [

View File

@ -5,13 +5,14 @@
--- ---
[![](https://img.shields.io/discord/809154715984199690?label=Discord&style=for-the-badge)](https://discord.gg/jg33eMhnj6) [![](https://img.shields.io/discord/809154715984199690?label=Discord&style=for-the-badge)](https://discord.gg/jg33eMhnj6)
[![CI Build status](https://img.shields.io/azure-devops/build/beryjuorg/authentik/6?style=for-the-badge)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=6) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/goauthentik/authentik/authentik-ci-main?label=core%20build&style=for-the-badge)
[![Tests](https://img.shields.io/azure-devops/tests/beryjuorg/authentik/6?compact_message&style=for-the-badge)](https://dev.azure.com/beryjuorg/authentik/_build?definitionId=6) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/goauthentik/authentik/authentik-ci-web?label=web%20build&style=for-the-badge)
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/goauthentik/authentik/authentik-ci-outpost?label=outpost%20build&style=for-the-badge)
[![Code Coverage](https://img.shields.io/codecov/c/gh/goauthentik/authentik?style=for-the-badge)](https://codecov.io/gh/goauthentik/authentik) [![Code Coverage](https://img.shields.io/codecov/c/gh/goauthentik/authentik?style=for-the-badge)](https://codecov.io/gh/goauthentik/authentik)
![Docker pulls](https://img.shields.io/docker/pulls/beryju/authentik.svg?style=for-the-badge) ![Docker pulls](https://img.shields.io/docker/pulls/beryju/authentik.svg?style=for-the-badge)
![Latest version](https://img.shields.io/docker/v/beryju/authentik?sort=semver&style=for-the-badge) ![Latest version](https://img.shields.io/docker/v/beryju/authentik?sort=semver&style=for-the-badge)
![LGTM Grade](https://img.shields.io/lgtm/grade/python/github/goauthentik/authentik?style=for-the-badge) ![LGTM Grade](https://img.shields.io/lgtm/grade/python/github/goauthentik/authentik?style=for-the-badge)
[Transifex](https://www.transifex.com/beryjuorg/authentik/) [![](https://img.shields.io/badge/Help%20translate-transifex-blue?style=for-the-badge)](https://www.transifex.com/beryjuorg/authentik/)
## What is authentik? ## What is authentik?

View File

@ -1,3 +1,3 @@
"""authentik""" """authentik"""
__version__ = "2021.8.3" __version__ = "2021.8.4"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -1,8 +1,10 @@
"""authentik api urls""" """authentik api urls"""
from django.urls import include, path from django.urls import include, path
from authentik.api.v2.urls import urlpatterns as v2_urls from authentik.api.v3.urls import urlpatterns as v3_urls
urlpatterns = [ urlpatterns = [
path("v2beta/", include(v2_urls)), # Remove in 2022.1
path("v2beta/", include(v3_urls)),
path("v3/", include(v3_urls)),
] ]

View File

@ -4,16 +4,44 @@ from json import loads
from django.conf import settings from django.conf import settings
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.views.generic.base import View
from requests import post from requests import post
from requests.exceptions import RequestException from requests.exceptions import RequestException
from rest_framework.authentication import SessionAuthentication
from rest_framework.parsers import BaseParser
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 authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
class SentryTunnelView(View): class PlainTextParser(BaseParser):
"""Plain text parser."""
media_type = "text/plain"
def parse(self, stream, media_type=None, parser_context=None) -> str:
"""Simply return a string representing the body of the request."""
return stream.read()
class CsrfExemptSessionAuthentication(SessionAuthentication):
"""CSRF-exempt Session authentication"""
def enforce_csrf(self, request: Request):
return # To not perform the csrf check previously happening
class SentryTunnelView(APIView):
"""Sentry tunnel, to prevent ad blockers from blocking sentry""" """Sentry tunnel, to prevent ad blockers from blocking sentry"""
serializer_class = None
parser_classes = [PlainTextParser]
throttle_classes = [AnonRateThrottle]
permission_classes = [AllowAny]
authentication_classes = [CsrfExemptSessionAuthentication]
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Sentry tunnel, to prevent ad blockers from blocking sentry""" """Sentry tunnel, to prevent ad blockers from blocking sentry"""
# Only allow usage of this endpoint when error reporting is enabled # Only allow usage of this endpoint when error reporting is enabled

View File

@ -1,4 +1,4 @@
"""api v2 urls""" """api v3 urls"""
from django.urls import path from django.urls import path
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.views import SpectacularAPIView from drf_spectacular.views import SpectacularAPIView
@ -10,8 +10,8 @@ from authentik.admin.api.system import SystemView
from authentik.admin.api.tasks import TaskViewSet from authentik.admin.api.tasks import TaskViewSet
from authentik.admin.api.version import VersionView from authentik.admin.api.version import VersionView
from authentik.admin.api.workers import WorkerView from authentik.admin.api.workers import WorkerView
from authentik.api.v2.config import ConfigView from authentik.api.v3.config import ConfigView
from authentik.api.v2.sentry import SentryTunnelView from authentik.api.v3.sentry import SentryTunnelView
from authentik.api.views import APIBrowserView from authentik.api.views import APIBrowserView
from authentik.core.api.applications import ApplicationViewSet from authentik.core.api.applications import ApplicationViewSet
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet

View File

@ -23,7 +23,7 @@ from authentik.managed.api import ManagedSerializer
class TokenSerializer(ManagedSerializer, ModelSerializer): class TokenSerializer(ManagedSerializer, ModelSerializer):
"""Token Serializer""" """Token Serializer"""
user_obj = UserSerializer(required=False) user_obj = UserSerializer(required=False, source="user")
def validate(self, attrs: dict[Any, str]) -> dict[Any, str]: def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
"""Ensure only API or App password tokens are created.""" """Ensure only API or App password tokens are created."""

View File

@ -11,6 +11,7 @@ from django.core.cache import cache
from prometheus_client import Gauge from prometheus_client import Gauge
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.utils.errors import exception_to_string
GAUGE_TASKS = Gauge( GAUGE_TASKS = Gauge(
"authentik_system_tasks", "authentik_system_tasks",
@ -174,9 +175,7 @@ class MonitoredTask(Task):
).save(self.result_timeout_hours) ).save(self.result_timeout_hours)
Event.new( Event.new(
EventAction.SYSTEM_TASK_EXCEPTION, EventAction.SYSTEM_TASK_EXCEPTION,
message=( message=(f"Task {self.__name__} encountered an error: {exception_to_string(exc)}"),
f"Task {self.__name__} encountered an error: " "\n".join(self._result.messages)
),
).save() ).save()
return super().on_failure(exc, task_id, args, kwargs, einfo=einfo) return super().on_failure(exc, task_id, args, kwargs, einfo=einfo)

View File

@ -31,6 +31,7 @@ class FlowPlanProcess(PROCESS_CLASS): # pragma: no cover
self.request = RequestFactory().get("/") self.request = RequestFactory().get("/")
def run(self): def run(self):
"""Execute 1000 flow plans"""
print(f"Proc {self.index} Running") print(f"Proc {self.index} Running")
def test_inner(): def test_inner():

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.6 on 2021-08-30 14:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_flows", "0023_alter_flow_background"),
]
operations = [
migrations.AlterField(
model_name="flow",
name="compatibility_mode",
field=models.BooleanField(
default=False,
help_text="Enable compatibility mode, increases compatibility with password managers on mobile devices.",
),
),
]

View File

@ -125,7 +125,7 @@ class Flow(SerializerModel, PolicyBindingModel):
) )
compatibility_mode = models.BooleanField( compatibility_mode = models.BooleanField(
default=True, default=False,
help_text=_( help_text=_(
"Enable compatibility mode, increases compatibility with " "Enable compatibility mode, increases compatibility with "
"password managers on mobile devices." "password managers on mobile devices."

View File

@ -2,10 +2,10 @@
from unittest.mock import MagicMock, PropertyMock, patch from unittest.mock import MagicMock, PropertyMock, patch
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -37,7 +37,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse):
TO_STAGE_RESPONSE_MOCK = MagicMock(side_effect=to_stage_response) TO_STAGE_RESPONSE_MOCK = MagicMock(side_effect=to_stage_response)
class TestFlowExecutor(TestCase): class TestFlowExecutor(APITestCase):
"""Test views logic""" """Test views logic"""
def setUp(self): def setUp(self):

View File

@ -1,5 +1,5 @@
"""flow views tests""" """flow views tests"""
from django.test import Client, TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from authentik.flows.models import Flow, FlowDesignation from authentik.flows.models import Flow, FlowDesignation
@ -10,9 +10,6 @@ from authentik.flows.views import SESSION_KEY_PLAN
class TestHelperView(TestCase): class TestHelperView(TestCase):
"""Test helper views logic""" """Test helper views logic"""
def setUp(self):
self.client = Client()
def test_default_view(self): def test_default_view(self):
"""Test that ToDefaultFlow returns the expected URL""" """Test that ToDefaultFlow returns the expected URL"""
flow = Flow.objects.filter( flow = Flow.objects.filter(

View File

@ -8,8 +8,11 @@ from structlog.stdlib import get_logger
from authentik.policies.models import Policy from authentik.policies.models import Policy
from authentik.policies.types import PolicyRequest, PolicyResult from authentik.policies.types import PolicyRequest, PolicyResult
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
LOGGER = get_logger() LOGGER = get_logger()
RE_LOWER = re.compile("[a-z]")
RE_UPPER = re.compile("[A-Z]")
class PasswordPolicy(Policy): class PasswordPolicy(Policy):
@ -38,31 +41,39 @@ class PasswordPolicy(Policy):
return "ak-policy-password-form" return "ak-policy-password-form"
def passes(self, request: PolicyRequest) -> PolicyResult: def passes(self, request: PolicyRequest) -> PolicyResult:
if self.password_field not in request.context: if (
self.password_field not in request.context
and self.password_field not in request.context.get(PLAN_CONTEXT_PROMPT, {})
):
LOGGER.warning( LOGGER.warning(
"Password field not set in Policy Request", "Password field not set in Policy Request",
field=self.password_field, field=self.password_field,
fields=request.context.keys(), fields=request.context.keys(),
prompt_fields=request.context.get(PLAN_CONTEXT_PROMPT, {}).keys(),
) )
return PolicyResult(False, _("Password not set in context")) return PolicyResult(False, _("Password not set in context"))
password = request.context[self.password_field]
filter_regex = [] if self.password_field in request.context:
if self.amount_lowercase > 0: password = request.context[self.password_field]
filter_regex.append(r"[a-z]{%d,}" % self.amount_lowercase) else:
if self.amount_uppercase > 0: password = request.context[PLAN_CONTEXT_PROMPT][self.password_field]
filter_regex.append(r"[A-Z]{%d,}" % self.amount_uppercase)
if self.amount_symbols > 0:
filter_regex.append(r"[%s]{%d,}" % (self.symbol_charset, self.amount_symbols))
full_regex = "|".join(filter_regex)
LOGGER.debug("Built regex", regexp=full_regex)
result = bool(re.compile(full_regex).match(password))
result = result and len(password) >= self.length_min if len(password) < self.length_min:
LOGGER.debug("password failed", reason="length", p=password)
return PolicyResult(False, self.error_message)
if not result: if self.amount_lowercase > 0 and len(RE_LOWER.findall(password)) < self.amount_lowercase:
return PolicyResult(result, self.error_message) LOGGER.debug("password failed", reason="amount_lowercase", p=password)
return PolicyResult(result) return PolicyResult(False, self.error_message)
if self.amount_uppercase > 0 and len(RE_UPPER.findall(password)) < self.amount_lowercase:
LOGGER.debug("password failed", reason="amount_uppercase", p=password)
return PolicyResult(False, self.error_message)
regex = re.compile(r"[%s]" % self.symbol_charset)
if self.amount_symbols > 0 and len(regex.findall(password)) < self.amount_symbols:
LOGGER.debug("password failed", reason="amount_symbols", p=password)
return PolicyResult(False, self.error_message)
return PolicyResult(True)
class Meta: class Meta:

View File

@ -1,57 +0,0 @@
"""Password Policy tests"""
from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from authentik.policies.password.models import PasswordPolicy
from authentik.policies.types import PolicyRequest, PolicyResult
class TestPasswordPolicy(TestCase):
"""Test Password Policy"""
def test_invalid(self):
"""Test without password"""
policy = PasswordPolicy.objects.create(
name="test_invalid",
amount_uppercase=1,
amount_lowercase=2,
amount_symbols=3,
length_min=24,
error_message="test message",
)
request = PolicyRequest(get_anonymous_user())
result: PolicyResult = policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages[0], "Password not set in context")
def test_false(self):
"""Failing password case"""
policy = PasswordPolicy.objects.create(
name="test_false",
amount_uppercase=1,
amount_lowercase=2,
amount_symbols=3,
length_min=24,
error_message="test message",
)
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "test"
result: PolicyResult = policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
def test_true(self):
"""Positive password case"""
policy = PasswordPolicy.objects.create(
name="test_true",
amount_uppercase=1,
amount_lowercase=2,
amount_symbols=3,
length_min=3,
error_message="test message",
)
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "Test()!"
result: PolicyResult = policy.passes(request)
self.assertTrue(result.passing)
self.assertEqual(result.messages, tuple())

View File

@ -0,0 +1,80 @@
"""Password flow tests"""
from django.urls.base import reverse
from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.policies.password.models import PasswordPolicy
from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
class TestPasswordPolicyFlow(APITestCase):
"""Test Password Policy"""
def setUp(self) -> None:
self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.flow = Flow.objects.create(
name="test-prompt",
slug="test-prompt",
designation=FlowDesignation.AUTHENTICATION,
)
password_prompt = Prompt.objects.create(
field_key="password",
label="PASSWORD_LABEL",
type=FieldTypes.PASSWORD,
required=True,
placeholder="PASSWORD_PLACEHOLDER",
)
self.policy = PasswordPolicy.objects.create(
name="test_true",
amount_uppercase=1,
amount_lowercase=2,
amount_symbols=3,
length_min=3,
error_message="test message",
)
stage = PromptStage.objects.create(name="prompt-stage")
stage.validation_policies.set([self.policy])
stage.fields.set(
[
password_prompt,
]
)
FlowStageBinding.objects.create(target=self.flow, stage=stage, order=2)
def test_prompt_data(self):
"""Test policy attached to a prompt stage"""
response = self.client.post(
reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
{"password": "akadmin"},
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(
force_str(response.content),
{
"component": "ak-stage-prompt",
"fields": [
{
"field_key": "password",
"label": "PASSWORD_LABEL",
"order": 0,
"placeholder": "PASSWORD_PLACEHOLDER",
"required": True,
"type": "password",
}
],
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"response_errors": {
"non_field_errors": [{"code": "invalid", "string": self.policy.error_message}]
},
"type": ChallengeTypes.NATIVE.value,
},
)

View File

@ -0,0 +1,68 @@
"""Password Policy tests"""
from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from authentik.lib.generators import generate_key
from authentik.policies.password.models import PasswordPolicy
from authentik.policies.types import PolicyRequest, PolicyResult
class TestPasswordPolicy(TestCase):
"""Test Password Policy"""
def setUp(self) -> None:
self.policy = PasswordPolicy.objects.create(
name="test_false",
amount_uppercase=1,
amount_lowercase=2,
amount_symbols=3,
length_min=24,
error_message="test message",
)
def test_invalid(self):
"""Test without password"""
request = PolicyRequest(get_anonymous_user())
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages[0], "Password not set in context")
def test_failed_length(self):
"""Password too short"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "test"
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
def test_failed_lowercase(self):
"""not enough lowercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "TTTTTTTTTTTTTTTTTTTTTTTe"
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
def test_failed_uppercase(self):
"""not enough uppercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "tttttttttttttttttttttttE"
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
def test_failed_symbols(self):
"""not enough uppercase"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = "TETETETETETETETETETETETETe!!!"
result: PolicyResult = self.policy.passes(request)
self.assertFalse(result.passing)
self.assertEqual(result.messages, ("test message",))
def test_true(self):
"""Positive password case"""
request = PolicyRequest(get_anonymous_user())
request.context["password"] = generate_key() + "ee!!!"
result: PolicyResult = self.policy.passes(request)
self.assertTrue(result.passing)
self.assertEqual(result.messages, tuple())

View File

@ -61,9 +61,10 @@ class IngressReconciler(KubernetesObjectReconciler[NetworkingV1beta1Ingress]):
have_hosts.sort() have_hosts.sort()
have_hosts_tls = [] have_hosts_tls = []
for tls_config in current.spec.tls: if current.spec.tls:
if tls_config and tls_config.hosts: for tls_config in current.spec.tls:
have_hosts_tls += tls_config.hosts if tls_config and tls_config.hosts:
have_hosts_tls += tls_config.hosts
have_hosts_tls.sort() have_hosts_tls.sort()
if have_hosts != expected_hosts: if have_hosts != expected_hosts:

View File

@ -96,6 +96,7 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
def get_reference_object(self) -> TraefikMiddleware: def get_reference_object(self) -> TraefikMiddleware:
"""Get deployment object for outpost""" """Get deployment object for outpost"""
port = 9000 if self.is_embedded else 4180
return TraefikMiddleware( return TraefikMiddleware(
apiVersion=f"{CRD_GROUP}/{CRD_VERSION}", apiVersion=f"{CRD_GROUP}/{CRD_VERSION}",
kind="Middleware", kind="Middleware",
@ -106,7 +107,7 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
), ),
spec=TraefikMiddlewareSpec( spec=TraefikMiddlewareSpec(
forwardAuth=TraefikMiddlewareSpecForwardAuth( forwardAuth=TraefikMiddlewareSpecForwardAuth(
address=f"http://{self.name}.{self.namespace}:4180/akprox/auth?traefik", address=f"http://{self.name}.{self.namespace}:{port}/akprox/auth?traefik",
authResponseHeaders=[ authResponseHeaders=[
"Set-Cookie", "Set-Cookie",
"X-Auth-Username", "X-Auth-Username",

View File

@ -150,9 +150,20 @@ SPECTACULAR_SETTINGS = {
"DESCRIPTION": "Making authentication simple.", "DESCRIPTION": "Making authentication simple.",
"VERSION": __version__, "VERSION": __version__,
"COMPONENT_SPLIT_REQUEST": True, "COMPONENT_SPLIT_REQUEST": True,
"SCHEMA_PATH_PREFIX": "/api/v([0-9]+(beta)?)",
"SCHEMA_PATH_PREFIX_TRIM": True,
"SERVERS": [
{
"url": "http://authentik.tld/api/v3/",
},
{
"url": "http://authentik.tld/api/v2beta/",
},
],
"CONTACT": { "CONTACT": {
"email": "hello@beryju.org", "email": "hello@beryju.org",
}, },
"AUTHENTICATION_WHITELIST": ["authentik.api.authentication.TokenAuthentication"],
"LICENSE": { "LICENSE": {
"name": "GNU GPLv3", "name": "GNU GPLv3",
"url": "https://github.com/goauthentik/authentik/blob/master/LICENSE", "url": "https://github.com/goauthentik/authentik/blob/master/LICENSE",
@ -180,6 +191,9 @@ REST_FRAMEWORK = {
"rest_framework.filters.OrderingFilter", "rest_framework.filters.OrderingFilter",
"rest_framework.filters.SearchFilter", "rest_framework.filters.SearchFilter",
], ],
"DEFAULT_PARSER_CLASSES": [
"rest_framework.parsers.JSONParser",
],
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.DjangoObjectPermissions",), "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.DjangoObjectPermissions",),
"DEFAULT_AUTHENTICATION_CLASSES": ( "DEFAULT_AUTHENTICATION_CLASSES": (
"authentik.api.authentication.TokenAuthentication", "authentik.api.authentication.TokenAuthentication",
@ -189,6 +203,7 @@ REST_FRAMEWORK = {
"rest_framework.renderers.JSONRenderer", "rest_framework.renderers.JSONRenderer",
], ],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"TEST_REQUEST_DEFAULT_FORMAT": "json",
} }
REDIS_PROTOCOL_PREFIX = "redis://" REDIS_PROTOCOL_PREFIX = "redis://"
@ -372,6 +387,7 @@ if CONFIG.y("postgresql.s3_backup"):
"default_acl": "private", "default_acl": "private",
"endpoint_url": CONFIG.y("postgresql.s3_backup.host"), "endpoint_url": CONFIG.y("postgresql.s3_backup.host"),
"location": CONFIG.y("postgresql.s3_backup.location", ""), "location": CONFIG.y("postgresql.s3_backup.location", ""),
"verify": not CONFIG.y_bool("postgresql.s3_backup.insecure_skip_verify", False),
} }
j_print( j_print(
"Database backup to S3 is configured", "Database backup to S3 is configured",

View File

@ -2,17 +2,13 @@
from base64 import b64encode from base64 import b64encode
from django.conf import settings from django.conf import settings
from django.test import Client, TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
class TestRoot(TestCase): class TestRoot(TestCase):
"""Test root application""" """Test root application"""
def setUp(self):
super().setUp()
self.client = Client()
def test_monitoring_error(self): def test_monitoring_error(self):
"""Test monitoring without any credentials""" """Test monitoring without any credentials"""
response = self.client.get(reverse("metrics")) response = self.client.get(reverse("metrics"))

View File

@ -1,5 +1,5 @@
"""Twitter Type tests""" """Twitter Type tests"""
from django.test import Client, TestCase from django.test import TestCase
from authentik.sources.oauth.models import OAuthSource from authentik.sources.oauth.models import OAuthSource
from authentik.sources.oauth.types.twitter import TwitterOAuthCallback from authentik.sources.oauth.types.twitter import TwitterOAuthCallback
@ -92,7 +92,6 @@ class TestTypeGitHub(TestCase):
"""OAuth Source tests""" """OAuth Source tests"""
def setUp(self): def setUp(self):
self.client = Client()
self.source = OAuthSource.objects.create( self.source = OAuthSource.objects.create(
name="test", name="test",
slug="test", slug="test",

View File

@ -51,20 +51,30 @@ def get_webauthn_challenge(request: HttpRequest, device: WebAuthnDevice) -> dict
# for the reasons outlined in the comment in webauthn_begin_activate. # for the reasons outlined in the comment in webauthn_begin_activate.
request.session["challenge"] = challenge.rstrip("=") request.session["challenge"] = challenge.rstrip("=")
webauthn_user = WebAuthnUser( assertion = {}
device.user.uid, user = device.user
device.user.username,
device.user.name,
device.user.avatar,
device.credential_id,
device.public_key,
device.sign_count,
device.rp_id,
)
webauthn_assertion_options = WebAuthnAssertionOptions(webauthn_user, challenge) # We want all the user's WebAuthn devices and merge their challenges
for user_device in WebAuthnDevice.objects.filter(user=device.user).order_by("name"):
webauthn_user = WebAuthnUser(
user.uid,
user.username,
user.name,
user.avatar,
user_device.credential_id,
user_device.public_key,
user_device.sign_count,
user_device.rp_id,
)
webauthn_assertion_options = WebAuthnAssertionOptions(webauthn_user, challenge)
if assertion == {}:
assertion = webauthn_assertion_options.assertion_dict
else:
assertion["allowCredentials"] += webauthn_assertion_options.assertion_dict.get(
"allowCredentials"
)
return webauthn_assertion_options.assertion_dict return assertion
def validate_challenge_code(code: str, request: HttpRequest, user: User) -> str: def validate_challenge_code(code: str, request: HttpRequest, user: User) -> str:

View File

@ -20,8 +20,6 @@ from authentik.stages.authenticator_validate.models import AuthenticatorValidate
LOGGER = get_logger() LOGGER = get_logger()
PER_DEVICE_CLASSES = [DeviceClasses.WEBAUTHN]
class AuthenticatorValidationChallenge(WithUserInfoChallenge): class AuthenticatorValidationChallenge(WithUserInfoChallenge):
"""Authenticator challenge""" """Authenticator challenge"""
@ -91,9 +89,9 @@ class AuthenticatorValidateStageView(ChallengeStageView):
if device_class not in stage.device_classes: if device_class not in stage.device_classes:
LOGGER.debug("device class not allowed", device_class=device_class) LOGGER.debug("device class not allowed", device_class=device_class)
continue continue
# Ensure only classes in PER_DEVICE_CLASSES are returned per device # Ensure only one challenge per device class
# otherwise only return a single challenge # WebAuthn does another device loop to find all webuahtn devices
if device_class in seen_classes and device_class not in PER_DEVICE_CLASSES: if device_class in seen_classes:
continue continue
if device_class not in seen_classes: if device_class not in seen_classes:
seen_classes.append(device_class) seen_classes.append(device_class)

View File

@ -1,12 +1,12 @@
"""Test validator stage""" """Test validator stage"""
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.urls.base import reverse from django.urls.base import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django_otp.plugins.otp_totp.models import TOTPDevice from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -26,7 +26,7 @@ from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
from authentik.stages.identification.models import IdentificationStage, UserFields from authentik.stages.identification.models import IdentificationStage, UserFields
class AuthenticatorValidateStageTests(TestCase): class AuthenticatorValidateStageTests(APITestCase):
"""Test validator stage""" """Test validator stage"""
def setUp(self) -> None: def setUp(self) -> None:

View File

@ -1,7 +1,7 @@
"""captcha tests""" """captcha tests"""
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -16,13 +16,12 @@ RECAPTCHA_PUBLIC_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
RECAPTCHA_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" RECAPTCHA_PRIVATE_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
class TestCaptchaStage(TestCase): class TestCaptchaStage(APITestCase):
"""Captcha tests""" """Captcha tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create_user(username="unittest", email="test@beryju.org") self.user = User.objects.create_user(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-captcha", name="test-captcha",

View File

@ -1,9 +1,9 @@
"""consent tests""" """consent tests"""
from time import sleep from time import sleep
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import Application, User from authentik.core.models import Application, User
from authentik.core.tasks import clean_expired_models from authentik.core.tasks import clean_expired_models
@ -15,7 +15,7 @@ from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
class TestConsentStage(TestCase): class TestConsentStage(APITestCase):
"""Consent tests""" """Consent tests"""
def setUp(self): def setUp(self):
@ -25,7 +25,6 @@ class TestConsentStage(TestCase):
name="test-application", name="test-application",
slug="test-application", slug="test-application",
) )
self.client = Client()
def test_always_required(self): def test_always_required(self):
"""Test always required consent""" """Test always required consent"""

View File

@ -1,7 +1,7 @@
"""deny tests""" """deny tests"""
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -12,13 +12,12 @@ from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.deny.models import DenyStage from authentik.stages.deny.models import DenyStage
class TestUserDenyStage(TestCase): class TestUserDenyStage(APITestCase):
"""Deny tests""" """Deny tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org") self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-logout", name="test-logout",

View File

@ -1,7 +1,7 @@
"""dummy tests""" """dummy tests"""
from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -9,7 +9,7 @@ from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding
from authentik.stages.dummy.models import DummyStage from authentik.stages.dummy.models import DummyStage
class TestDummyStage(TestCase): class TestDummyStage(APITestCase):
"""Dummy tests""" """Dummy tests"""
def setUp(self): def setUp(self):

View File

@ -4,8 +4,8 @@ from unittest.mock import MagicMock, patch
from django.core import mail from django.core import mail
from django.core.mail.backends.locmem import EmailBackend from django.core.mail.backends.locmem import EmailBackend
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
@ -16,13 +16,12 @@ from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.email.models import EmailStage from authentik.stages.email.models import EmailStage
class TestEmailStageSending(TestCase): class TestEmailStageSending(APITestCase):
"""Email tests""" """Email tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create_user(username="unittest", email="test@beryju.org") self.user = User.objects.create_user(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-email", name="test-email",

View File

@ -2,10 +2,10 @@
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from django.core import mail from django.core import mail
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.http import urlencode from django.utils.http import urlencode
from rest_framework.test import APITestCase
from authentik.core.models import Token, User from authentik.core.models import Token, User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -17,13 +17,12 @@ from authentik.stages.email.models import EmailStage
from authentik.stages.email.stage import QS_KEY_TOKEN from authentik.stages.email.stage import QS_KEY_TOKEN
class TestEmailStage(TestCase): class TestEmailStage(APITestCase):
"""Email tests""" """Email tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create_user(username="unittest", email="test@beryju.org") self.user = User.objects.create_user(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-email", name="test-email",

View File

@ -1,7 +1,7 @@
"""identification tests""" """identification tests"""
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -13,7 +13,7 @@ from authentik.stages.password import BACKEND_INBUILT
from authentik.stages.password.models import PasswordStage from authentik.stages.password.models import PasswordStage
class TestIdentificationStage(TestCase): class TestIdentificationStage(APITestCase):
"""Identification tests""" """Identification tests"""
def setUp(self): def setUp(self):
@ -22,7 +22,6 @@ class TestIdentificationStage(TestCase):
self.user = User.objects.create_user( self.user = User.objects.create_user(
username="unittest", email="test@beryju.org", password=self.password username="unittest", email="test@beryju.org", password=self.password
) )
self.client = Client()
# OAuthSource for the login view # OAuthSource for the login view
source = OAuthSource.objects.create(name="test", slug="test") source = OAuthSource.objects.create(name="test", slug="test")

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.6 on 2021-09-01 12:11
from django.db import migrations, models
import authentik.core.models
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_invitation", "0004_invitation_single_use"),
]
operations = [
migrations.AddField(
model_name="invitation",
name="expiring",
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name="invitation",
name="expires",
field=models.DateTimeField(default=authentik.core.models.default_token_duration),
),
]

View File

@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from rest_framework.serializers import BaseSerializer from rest_framework.serializers import BaseSerializer
from authentik.core.models import User from authentik.core.models import ExpiringModel, User
from authentik.flows.models import Stage from authentik.flows.models import Stage
@ -48,7 +48,7 @@ class InvitationStage(Stage):
verbose_name_plural = _("Invitation Stages") verbose_name_plural = _("Invitation Stages")
class Invitation(models.Model): class Invitation(ExpiringModel):
"""Single-use invitation link""" """Single-use invitation link"""
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
@ -59,7 +59,6 @@ class Invitation(models.Model):
) )
created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_by = models.ForeignKey(User, on_delete=models.CASCADE)
expires = models.DateTimeField(default=None, blank=True, null=True)
fixed_data = models.JSONField( fixed_data = models.JSONField(
default=dict, default=dict,
blank=True, blank=True,

View File

@ -1,7 +1,6 @@
"""invitation tests""" """invitation tests"""
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.http import urlencode from django.utils.http import urlencode
@ -21,13 +20,12 @@ from authentik.stages.password import BACKEND_INBUILT
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
class TestUserLoginStage(TestCase): class TestUserLoginStage(APITestCase):
"""Login tests""" """Login tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org") self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-invitation", name="test-invitation",

View File

@ -2,9 +2,9 @@
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -20,7 +20,7 @@ from authentik.stages.password.models import PasswordStage
MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test")) MOCK_BACKEND_AUTHENTICATE = MagicMock(side_effect=PermissionDenied("test"))
class TestPasswordStage(TestCase): class TestPasswordStage(APITestCase):
"""Password tests""" """Password tests"""
def setUp(self): def setUp(self):
@ -29,7 +29,6 @@ class TestPasswordStage(TestCase):
self.user = User.objects.create_user( self.user = User.objects.create_user(
username="unittest", email="test@beryju.org", password=self.password username="unittest", email="test@beryju.org", password=self.password
) )
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-password", name="test-password",

View File

@ -1,10 +1,10 @@
"""Prompt tests""" """Prompt tests"""
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.exceptions import ErrorDetail from rest_framework.exceptions import ErrorDetail
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -17,13 +17,12 @@ from authentik.stages.prompt.models import FieldTypes, Prompt, PromptStage
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT, PromptChallengeResponse from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT, PromptChallengeResponse
class TestPromptStage(TestCase): class TestPromptStage(APITestCase):
"""Prompt tests""" """Prompt tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org") self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-prompt", name="test-prompt",
@ -97,7 +96,6 @@ class TestPromptStage(TestCase):
static_prompt, static_prompt,
] ]
) )
self.stage.save()
self.prompt_data = { self.prompt_data = {
username_prompt.field_key: "test-username", username_prompt.field_key: "test-username",

View File

@ -1,9 +1,9 @@
"""delete tests""" """delete tests"""
from unittest.mock import patch from unittest.mock import patch
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -15,14 +15,13 @@ from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.user_delete.models import UserDeleteStage from authentik.stages.user_delete.models import UserDeleteStage
class TestUserDeleteStage(TestCase): class TestUserDeleteStage(APITestCase):
"""Delete tests""" """Delete tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.username = "qerqwerqrwqwerwq" self.username = "qerqwerqrwqwerwq"
self.user = User.objects.create(username=self.username, email="test@beryju.org") self.user = User.objects.create(username=self.username, email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-delete", name="test-delete",

View File

@ -2,9 +2,9 @@
from time import sleep from time import sleep
from unittest.mock import patch from unittest.mock import patch
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -16,13 +16,12 @@ from authentik.flows.views import SESSION_KEY_PLAN
from authentik.stages.user_login.models import UserLoginStage from authentik.stages.user_login.models import UserLoginStage
class TestUserLoginStage(TestCase): class TestUserLoginStage(APITestCase):
"""Login tests""" """Login tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org") self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-login", name="test-login",

View File

@ -1,7 +1,7 @@
"""logout tests""" """logout tests"""
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import User from authentik.core.models import User
from authentik.flows.challenge import ChallengeTypes from authentik.flows.challenge import ChallengeTypes
@ -14,13 +14,12 @@ from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
from authentik.stages.user_logout.models import UserLogoutStage from authentik.stages.user_logout.models import UserLogoutStage
class TestUserLogoutStage(TestCase): class TestUserLogoutStage(APITestCase):
"""Logout tests""" """Logout tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.user = User.objects.create(username="unittest", email="test@beryju.org") self.user = User.objects.create(username="unittest", email="test@beryju.org")
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-logout", name="test-logout",

View File

@ -3,9 +3,9 @@ import string
from random import SystemRandom from random import SystemRandom
from unittest.mock import patch from unittest.mock import patch
from django.test import Client, TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_str from django.utils.encoding import force_str
from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_SOURCES, Source, User, UserSourceConnection from authentik.core.models import USER_ATTRIBUTE_SOURCES, Source, User, UserSourceConnection
from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION
@ -19,13 +19,11 @@ from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
from authentik.stages.user_write.models import UserWriteStage from authentik.stages.user_write.models import UserWriteStage
class TestUserWriteStage(TestCase): class TestUserWriteStage(APITestCase):
"""Write tests""" """Write tests"""
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.client = Client()
self.flow = Flow.objects.create( self.flow = Flow.objects.create(
name="test-write", name="test-write",
slug="test-write", slug="test-write",

View File

@ -1,120 +0,0 @@
trigger:
batch: true
branches:
include:
- master
- next
- version-*
stages:
- stage: generate
jobs:
- job: generate_api
pool:
vmImage: 'ubuntu-latest'
steps:
- task: GoTool@0
inputs:
version: '1.16.3'
- task: CmdLine@2
inputs:
script: make gen-outpost
- task: PublishPipelineArtifact@1
inputs:
targetPath: 'api/'
artifact: 'go_api_client'
publishLocation: 'pipeline'
- stage: lint
jobs:
- job: golint
pool:
vmImage: 'ubuntu-latest'
steps:
- task: GoTool@0
inputs:
version: '1.16.3'
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'go_api_client'
path: "api/"
- task: CmdLine@2
inputs:
script: |
mkdir -p web/dist
mkdir -p website/help
touch web/dist/test website/help/test
docker run \
--rm \
-v $(pwd):/app \
-w /app \
golangci/golangci-lint:v1.39.0 \
golangci-lint run -v --timeout 200s
- stage: build_docker
jobs:
- job: proxy_build_docker
pool:
vmImage: 'ubuntu-latest'
steps:
- task: GoTool@0
inputs:
version: '1.16.3'
- task: Bash@3
inputs:
targetType: 'inline'
script: |
python ./scripts/az_do_set_branch.py
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-proxy'
command: 'build'
Dockerfile: 'proxy.Dockerfile'
buildContext: '$(Build.SourcesDirectory)'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-proxy'
command: 'push'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)
gh-$(Build.SourceVersion)
- job: ldap_build_docker
pool:
vmImage: 'ubuntu-latest'
steps:
- task: GoTool@0
inputs:
version: '1.16.3'
- task: Bash@3
inputs:
targetType: 'inline'
script: |
python ./scripts/az_do_set_branch.py
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-ldap'
command: 'build'
Dockerfile: 'ldap.Dockerfile'
buildContext: '$(Build.SourcesDirectory)'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)
gh-$(Build.SourceVersion)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/outpost-ldap'
command: 'push'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)
gh-$(Build.SourceVersion)

View File

@ -1,426 +0,0 @@
trigger:
batch: true
branches:
include:
- master
- next
- version-*
paths:
exclude:
- website
- outpost
resources:
- repo: self
variables:
- name: POSTGRES_DB
value: authentik
- name: POSTGRES_USER
value: authentik
- name: POSTGRES_PASSWORD
value: "EK-5jnKfjrGRm<77"
- group: coverage
stages:
- stage: Lint_and_test
jobs:
- job: pylint
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run pylint authentik tests lifecycle
- job: black
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run black --check authentik tests lifecycle
- job: isort
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run isort --check authentik tests lifecycle
- job: bandit
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run bandit -r authentik tests lifecycle
- job: pyright
pool:
vmImage: ubuntu-latest
steps:
- task: UseNode@1
inputs:
version: '12.x'
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: CmdLine@2
inputs:
script: npm install -g pyright@1.1.136
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: pipenv run pyright e2e lifecycle
- job: migrations
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run python -m lifecycle.migrate
- job: migrations_from_previous_release
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.8'
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
displayName: Prepare Last tagged release
inputs:
script: |
# Copy current, latest config to local
cp authentik/lib/default.yml local.env.yml
git checkout $(git describe --abbrev=0 --match 'version/*')
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
displayName: Migrate to last tagged release
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run python -m lifecycle.migrate
- task: CmdLine@2
displayName: Install current branch
inputs:
script: |
set -x
git checkout ${{ variables.branchName }}
pipenv sync --dev
- task: CmdLine@2
displayName: Migrate to current branch
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run python -m lifecycle.migrate
- job: coverage_unittest
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test
- task: CmdLine@2
inputs:
script: |
mkdir output-unittest
mv unittest.xml output-unittest/unittest.xml
mv .coverage output-unittest/coverage
- task: PublishPipelineArtifact@1
inputs:
targetPath: 'output-unittest/'
artifact: 'coverage-unittest'
publishLocation: 'pipeline'
- job: coverage_integration
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
- task: CmdLine@2
displayName: Install K3d and prepare
inputs:
script: |
wget -q -O - https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash
k3d cluster create
k3d kubeconfig write -o ~/.kube/config --overwrite
- task: CmdLine@2
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test-integration
- task: CmdLine@2
inputs:
script: |
mkdir output-integration
mv unittest.xml output-integration/unittest.xml
mv .coverage output-integration/coverage
- task: PublishPipelineArtifact@1
inputs:
targetPath: 'output-integration/'
artifact: 'coverage-integration'
publishLocation: 'pipeline'
- job: coverage_e2e
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: NodeTool@0
inputs:
versionSpec: '16.x'
- task: DockerCompose@0
displayName: Run services
inputs:
dockerComposeFile: 'scripts/ci.docker-compose.yml'
action: 'Run services'
buildImages: false
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev --python python3.9
- task: DockerCompose@0
displayName: Run ChromeDriver
inputs:
dockerComposeFile: 'tests/e2e/ci.docker-compose.yml'
action: 'Run a specific service'
serviceName: 'chrome'
- task: CmdLine@2
displayName: Build static files for e2e
inputs:
script: |
cd web
npm i
npm run build
- task: CmdLine@2
displayName: Run full test suite
inputs:
script: |
pipenv run python -m scripts.generate_ci_config
pipenv run make test-e2e
- task: CmdLine@2
condition: always()
displayName: Cleanup
inputs:
script: |
docker stop $(docker ps -aq)
docker container prune -f
- task: CmdLine@2
displayName: Prepare unittests and coverage for upload
inputs:
script: |
mkdir output-e2e
mv unittest.xml output-e2e/unittest.xml
mv .coverage output-e2e/coverage
- task: PublishPipelineArtifact@1
condition: failed()
displayName: Upload screenshots if selenium tests fail
inputs:
targetPath: 'selenium_screenshots/'
artifact: 'selenium screenshots'
publishLocation: 'pipeline'
- task: PublishPipelineArtifact@1
inputs:
targetPath: 'output-e2e/'
artifact: 'coverage-e2e'
publishLocation: 'pipeline'
- stage: test_combine
jobs:
- job: test_coverage_combine
pool:
vmImage: 'ubuntu-latest'
steps:
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'coverage-e2e'
path: "coverage-e2e/"
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'coverage-integration'
path: "coverage-integration/"
- task: DownloadPipelineArtifact@2
inputs:
buildType: 'current'
artifactName: 'coverage-unittest'
path: "coverage-unittest/"
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- task: CmdLine@2
inputs:
script: |
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
pipenv run coverage combine coverage-e2e/coverage coverage-unittest/coverage coverage-integration/coverage
pipenv run coverage xml
pipenv run coverage html
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: 'coverage.xml'
pathToSources: '$(System.DefaultWorkingDirectory)'
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: |
coverage-e2e/unittest.xml
coverage-integration/unittest.xml
coverage-unittest/unittest.xml
mergeTestResults: true
- task: CmdLine@2
inputs:
script: bash <(curl -s https://codecov.io/bash)
- stage: Build
jobs:
- job: build_server
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Bash@3
inputs:
targetType: 'inline'
script: |
python ./scripts/az_do_set_branch.py
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/server'
command: 'build'
Dockerfile: 'Dockerfile'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
- task: Docker@2
inputs:
containerRegistry: 'beryjuorg-harbor'
repository: 'authentik/server'
command: 'push'
tags: |
gh-$(branchName)
gh-$(branchName)-$(timestamp)

View File

@ -60,7 +60,9 @@ func main() {
for { for {
go attemptStartBackend(g) go attemptStartBackend(g)
ws.Start() ws.Start()
go attemptProxyStart(ws, u) if !config.G.Web.DisableEmbeddedOutpost {
go attemptProxyStart(ws, u)
}
<-ex <-ex
running = false running = false

View File

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

View File

@ -27,9 +27,10 @@ type RedisConfig struct {
} }
type WebConfig struct { type WebConfig struct {
Listen string `yaml:"listen"` Listen string `yaml:"listen"`
ListenTLS string `yaml:"listen_tls"` ListenTLS string `yaml:"listen_tls"`
LoadLocalFiles bool `yaml:"load_local_files" env:"AUTHENTIK_WEB_LOAD_LOCAL_FILES"` LoadLocalFiles bool `yaml:"load_local_files" env:"AUTHENTIK_WEB_LOAD_LOCAL_FILES"`
DisableEmbeddedOutpost bool `yaml:"disable_embedded_outpost" env:"AUTHENTIK_WEB__DISABLE_EMBEDDED_OUTPOST"`
} }
type PathsConfig struct { type PathsConfig struct {

View File

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

View File

@ -0,0 +1,32 @@
package ldap
import (
"net"
"time"
)
func (ls *LDAPServer) Close(boundDN string, conn net.Conn) error {
for _, p := range ls.providers {
p.delayDeleteUserInfo(boundDN)
}
return nil
}
func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
ticker := time.NewTicker(30 * time.Second)
quit := make(chan struct{})
go func() {
for {
select {
case <-ticker.C:
pi.boundUsersMutex.Lock()
delete(pi.boundUsers, dn)
pi.boundUsersMutex.Unlock()
close(quit)
case <-quit:
ticker.Stop()
return
}
}
}()
}

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"strings" "strings"
"time"
"github.com/getsentry/sentry-go" "github.com/getsentry/sentry-go"
goldap "github.com/go-ldap/ldap/v3" goldap "github.com/go-ldap/ldap/v3"
@ -83,7 +82,6 @@ func (pi *ProviderInstance) Bind(username string, req BindRequest) (ldap.LDAPRes
} }
uisp.Finish() uisp.Finish()
defer pi.boundUsersMutex.Unlock() defer pi.boundUsersMutex.Unlock()
pi.delayDeleteUserInfo(username)
return ldap.LDAPResultSuccess, nil return ldap.LDAPResultSuccess, nil
} }
@ -100,25 +98,6 @@ func (pi *ProviderInstance) SearchAccessCheck(user api.UserSelf) *string {
return nil return nil
} }
func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
ticker := time.NewTicker(30 * time.Second)
quit := make(chan struct{})
go func() {
for {
select {
case <-ticker.C:
pi.boundUsersMutex.Lock()
delete(pi.boundUsers, dn)
pi.boundUsersMutex.Unlock()
close(quit)
case <-quit:
ticker.Stop()
return
}
}
}()
}
func (pi *ProviderInstance) TimerFlowCacheExpiry() { func (pi *ProviderInstance) TimerFlowCacheExpiry() {
fe := outpost.NewFlowExecutor(context.Background(), pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{}) fe := outpost.NewFlowExecutor(context.Background(), pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{})
fe.Params.Add("goauthentik.io/outpost/ldap", "true") fe.Params.Add("goauthentik.io/outpost/ldap", "true")

View File

@ -83,5 +83,6 @@ func NewServer(ac *ak.APIController) *LDAPServer {
ls.defaultCert = &defaultCert ls.defaultCert = &defaultCert
s.BindFunc("", ls) s.BindFunc("", ls)
s.SearchFunc("", ls) s.SearchFunc("", ls)
s.CloseFunc("", ls)
return ls return ls
} }

View File

@ -25,6 +25,7 @@ type providerBundle struct {
Host string Host string
endSessionUrl string endSessionUrl string
Mode *api.ProxyMode
cert *tls.Certificate cert *tls.Certificate
@ -38,8 +39,8 @@ func intToPointer(i int) *int {
func (pb *providerBundle) replaceLocal(url string) string { func (pb *providerBundle) replaceLocal(url string) string {
if strings.HasPrefix(url, "http://localhost:8000") { if strings.HasPrefix(url, "http://localhost:8000") {
authentikHost, c := pb.s.ak.Outpost.Config["authentik_host"] authentikHost, c := pb.s.ak.Outpost.Config["authentik_host"]
if !c { if !c || authentikHost == "" {
pb.log.Warning("Outpost has localhost API Connection but no authentik_host is configured.") pb.log.Warning("Outpost has localhost/blank API Connection but no authentik_host is configured.")
return url return url
} }
f := strings.ReplaceAll(url, "http://localhost:8000", authentikHost.(string)) f := strings.ReplaceAll(url, "http://localhost:8000", authentikHost.(string))
@ -49,6 +50,10 @@ func (pb *providerBundle) replaceLocal(url string) string {
} }
func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.Options { func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.Options {
// We need to save the mode in the bundle
// Since for the embedded outpost we only switch for fully proxy providers
pb.Mode = provider.Mode
externalHost, err := url.Parse(provider.ExternalHost) externalHost, err := url.Parse(provider.ExternalHost)
if err != nil { if err != nil {
log.WithError(err).Warning("Failed to parse URL, skipping provider") log.WithError(err).Warning("Failed to parse URL, skipping provider")

View File

@ -121,7 +121,7 @@ func NewOAuthProxy(opts *options.Options, provider api.ProxyOutpostConfig, c *ht
redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix) redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
} }
logger.Printf("proxy instance configured for Client ID: %s", opts.ClientID) logger.WithField("auth_url", opts.GetProvider().Data().LoginURL).WithField("client_id", opts.ClientID).Info("proxy instance configured")
sessionChain := buildSessionChain(opts, sessionStore) sessionChain := buildSessionChain(opts, sessionStore)

View File

@ -6,6 +6,7 @@ import (
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"goauthentik.io/api"
"goauthentik.io/internal/utils/web" "goauthentik.io/internal/utils/web"
) )
@ -36,10 +37,12 @@ func (ws *WebServer) configureProxy() {
ws.m.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { ws.m.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
host := web.GetHost(r) host := web.GetHost(r)
if ws.ProxyServer != nil { if ws.ProxyServer != nil {
if _, ok := ws.ProxyServer.Handlers[host]; ok { if p, ok := ws.ProxyServer.Handlers[host]; ok {
ws.log.WithField("host", host).Trace("routing to proxy outpost") if *p.Mode == api.PROXYMODE_PROXY {
ws.ProxyServer.Handler(rw, r) ws.log.WithField("host", host).Trace("routing to proxy outpost")
return ws.ProxyServer.Handler(rw, r)
return
}
} }
} }
ws.log.WithField("host", host).Trace("routing to application server") ws.log.WithField("host", host).Trace("routing to application server")

2251
schema.yml

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
"""Helper script to get the actual branch name, docker safe"""
import os
from time import time
env_pr_branch = "SYSTEM_PULLREQUEST_SOURCEBRANCH"
default_branch = "BUILD_SOURCEBRANCHNAME"
branch_name = os.environ[default_branch]
if env_pr_branch in os.environ:
branch_name = os.environ[env_pr_branch].replace("/", "-")
print("##vso[task.setvariable variable=branchName]%s" % branch_name)
print("##vso[task.setvariable variable=timestamp]%s" % int(time()))

7
scripts/ci_prepare.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
docker-compose -f scripts/ci.docker-compose.yml up -d
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config
sudo pip install -U wheel pipenv
pipenv install --dev
pipenv run python -m scripts.generate_ci_config

View File

@ -0,0 +1,16 @@
"""Helper script to get the actual branch name, docker safe"""
import os
from time import time
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
sha = "GITHUB_SHA"
branch_name = os.environ[default_branch]
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
print("##[set-output name=branchName]%s" % branch_name)
print("##[set-output name=timestamp]%s" % int(time()))
print("##[set-output name=sha]%s" % os.environ[sha])

View File

@ -6,4 +6,3 @@ dist
coverage coverage
# don't lint generated code # don't lint generated code
api/ api/
azure-pipelines.yml

View File

@ -1,93 +0,0 @@
trigger:
batch: true
branches:
include:
- master
- next
- version-*
stages:
- stage: lint
jobs:
- job: eslint
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: 'web/'
- task: CmdLine@2
inputs:
script: make gen-web
- task: Npm@1
inputs:
command: 'custom'
workingDir: 'web/'
customCommand: 'run lint'
- job: prettier
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: 'web/'
- task: CmdLine@2
inputs:
script: make gen-web
- task: Npm@1
inputs:
command: 'custom'
workingDir: 'web/'
customCommand: 'run prettier-check'
- job: lit_analyse
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: 'web/'
- task: CmdLine@2
inputs:
script: make gen-web
- task: Npm@1
inputs:
command: 'custom'
workingDir: 'web/'
customCommand: 'run lit-analyse'
- stage: build_local
jobs:
- job: build
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- task: Npm@1
inputs:
command: 'install'
workingDir: 'web/'
- task: CmdLine@2
inputs:
script: make gen-web
- task: Npm@1
inputs:
command: 'custom'
workingDir: 'web/'
customCommand: 'run build'

306
web/package-lock.json generated
View File

@ -15,7 +15,7 @@
"@babel/preset-env": "^7.15.0", "@babel/preset-env": "^7.15.0",
"@babel/preset-typescript": "^7.15.0", "@babel/preset-typescript": "^7.15.0",
"@fortawesome/fontawesome-free": "^5.15.4", "@fortawesome/fontawesome-free": "^5.15.4",
"@goauthentik/api": "^2021.8.2-1629997023", "@goauthentik/api": "^2021.8.3-1630597235",
"@lingui/cli": "^3.10.2", "@lingui/cli": "^3.10.2",
"@lingui/core": "^3.10.4", "@lingui/core": "^3.10.4",
"@lingui/macro": "^3.10.2", "@lingui/macro": "^3.10.2",
@ -25,13 +25,13 @@
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^3.0.0", "@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5", "@rollup/plugin-typescript": "^8.2.5",
"@sentry/browser": "^6.11.0", "@sentry/browser": "^6.12.0",
"@sentry/tracing": "^6.11.0", "@sentry/tracing": "^6.12.0",
"@types/chart.js": "^2.9.34", "@types/chart.js": "^2.9.34",
"@types/codemirror": "5.60.2", "@types/codemirror": "5.60.2",
"@types/grecaptcha": "^3.0.3", "@types/grecaptcha": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^4.29.3", "@typescript-eslint/eslint-plugin": "^4.30.0",
"@typescript-eslint/parser": "^4.29.3", "@typescript-eslint/parser": "^4.30.0",
"@webcomponents/webcomponentsjs": "^2.6.0", "@webcomponents/webcomponentsjs": "^2.6.0",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",
@ -1689,9 +1689,9 @@
} }
}, },
"node_modules/@goauthentik/api": { "node_modules/@goauthentik/api": {
"version": "2021.8.2-1629997023", "version": "2021.8.3-1630597235",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.8.2-1629997023.tgz", "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.8.3-1630597235.tgz",
"integrity": "sha512-j80ZOgU+ZP40WD6PGJzxYmVKeGEcNJLjRqAQDvVmdfxA1++O6Ul1hYk02kSrDpQRbjdqmuO3u3a9s/HHKEvkrw==" "integrity": "sha512-W/AWgWE8WS8pWYPqy9JuzbzdYcTJ7aCjzgcLbOmD4roYTp1vP1znSZKx0zg9ZH7EBskFfDqm2eAueF1ktPC09g=="
}, },
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.5.0", "version": "0.5.0",
@ -2276,13 +2276,13 @@
} }
}, },
"node_modules/@sentry/browser": { "node_modules/@sentry/browser": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.12.0.tgz",
"integrity": "sha512-Qr2QRA0t5/S9QQqxzYKvM9W8prvmiWuldfwRX4hubovXzcXLgUi4WK0/H612wSbYZ4dNAEcQbtlxFWJNN4wxdg==", "integrity": "sha512-wsJi1NLOmfwtPNYxEC50dpDcVY7sdYckzwfqz1/zHrede1mtxpqSw+7iP4bHADOJXuF+ObYYTHND0v38GSXznQ==",
"dependencies": { "dependencies": {
"@sentry/core": "6.11.0", "@sentry/core": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2295,14 +2295,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/core": { "node_modules/@sentry/core": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz",
"integrity": "sha512-09TB+f3pqEq8LFahFWHO6I/4DxHo+NcS52OkbWMDqEi6oNZRD7PhPn3i14LfjsYVv3u3AESU8oxSEGbFrr2UjQ==", "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/minimal": "6.11.0", "@sentry/minimal": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2315,12 +2315,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/hub": { "node_modules/@sentry/hub": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz",
"integrity": "sha512-pT9hf+ZJfVFpoZopoC+yJmFNclr4NPqPcl2cgguqCHb69DklD1NxgBNWK8D6X05qjnNFDF991U6t1mxP9HrGuw==", "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==",
"dependencies": { "dependencies": {
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2333,12 +2333,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/minimal": { "node_modules/@sentry/minimal": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz",
"integrity": "sha512-XkZ7qrdlGp4IM/gjGxf1Q575yIbl5RvPbg+WFeekpo16Ufvzx37Mr8c2xsZaWosISVyE6eyFpooORjUlzy8EDw==", "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2351,14 +2351,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/tracing": { "node_modules/@sentry/tracing": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz",
"integrity": "sha512-9VA1/SY++WeoMQI4K6n/sYgIdRtCu9NLWqmGqu/5kbOtESYFgAt1DqSyqGCr00ZjQiC2s7tkDkTNZb38K6KytQ==", "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==",
"dependencies": { "dependencies": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/minimal": "6.11.0", "@sentry/minimal": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2371,19 +2371,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}, },
"node_modules/@sentry/types": { "node_modules/@sentry/types": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz",
"integrity": "sha512-gm5H9eZhL6bsIy/h3T+/Fzzz2vINhHhqd92CjHle3w7uXdTdFV98i2pDpErBGNTSNzbntqOMifYEB5ENtZAvcg==", "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@sentry/utils": { "node_modules/@sentry/utils": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz",
"integrity": "sha512-IOvyFHcnbRQxa++jO+ZUzRvFHEJ1cZjrBIQaNVc0IYF0twUOB5PTP6joTcix38ldaLeapaPZ9LGfudbvYvxkdg==", "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==",
"dependencies": { "dependencies": {
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"engines": { "engines": {
@ -2557,12 +2557,12 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.30.0.tgz",
"integrity": "sha512-tBgfA3K/3TsZY46ROGvoRxQr1wBkclbVqRQep97MjVHJzcRBURRY3sNFqLk0/Xr//BY5hM9H2p/kp+6qim85SA==", "integrity": "sha512-NgAnqk55RQ/SD+tZFD9aPwNSeHmDHHe5rtUyhIq0ZeCWZEvo4DK9rYz7v9HDuQZFvn320Ot+AikaCKMFKLlD0g==",
"dependencies": { "dependencies": {
"@typescript-eslint/experimental-utils": "4.29.3", "@typescript-eslint/experimental-utils": "4.30.0",
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.1.0", "regexpp": "^3.1.0",
@ -2601,14 +2601,14 @@
} }
}, },
"node_modules/@typescript-eslint/experimental-utils": { "node_modules/@typescript-eslint/experimental-utils": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.30.0.tgz",
"integrity": "sha512-ffIvbytTVWz+3keg+Sy94FG1QeOvmV9dP2YSdLFHw/ieLXWCa3U1TYu8IRCOpMv2/SPS8XqhM1+ou1YHsdzKrg==", "integrity": "sha512-K8RNIX9GnBsv5v4TjtwkKtqMSzYpjqAQg/oSphtxf3xxdt6T0owqnpojztjjTcatSteH3hLj3t/kklKx87NPqw==",
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.7", "@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/typescript-estree": "4.29.3", "@typescript-eslint/typescript-estree": "4.30.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0" "eslint-utils": "^3.0.0"
}, },
@ -2624,13 +2624,13 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.30.0.tgz",
"integrity": "sha512-jrHOV5g2u8ROghmspKoW7pN8T/qUzk0+DITun0MELptvngtMrwUJ1tv5zMI04CYVEUsSrN4jV7AKSv+I0y0EfQ==", "integrity": "sha512-HJ0XuluSZSxeboLU7Q2VQ6eLlCwXPBOGnA7CqgBnz2Db3JRQYyBDJgQnop6TZ+rsbSx5gEdWhw4rE4mDa1FnZg==",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/typescript-estree": "4.29.3", "@typescript-eslint/typescript-estree": "4.30.0",
"debug": "^4.3.1" "debug": "^4.3.1"
}, },
"engines": { "engines": {
@ -2650,12 +2650,12 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.30.0.tgz",
"integrity": "sha512-x+w8BLXO7iWPkG5mEy9bA1iFRnk36p/goVlYobVWHyDw69YmaH9q6eA+Fgl7kYHmFvWlebUTUfhtIg4zbbl8PA==", "integrity": "sha512-VJ/jAXovxNh7rIXCQbYhkyV2Y3Ac/0cVHP/FruTJSAUUm4Oacmn/nkN5zfWmWFEanN4ggP0vJSHOeajtHq3f8A==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/visitor-keys": "4.29.3" "@typescript-eslint/visitor-keys": "4.30.0"
}, },
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
@ -2666,9 +2666,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.30.0.tgz",
"integrity": "sha512-s1eV1lKNgoIYLAl1JUba8NhULmf+jOmmeFO1G5MN/RBCyyzg4TIOfIOICVNC06lor+Xmy4FypIIhFiJXOknhIg==", "integrity": "sha512-YKldqbNU9K4WpTNwBqtAerQKLLW/X2A/j4yw92e3ZJYLx+BpKLeheyzoPfzIXHfM8BXfoleTdiYwpsvVPvHrDw==",
"engines": { "engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1" "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
}, },
@ -2678,12 +2678,12 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.30.0.tgz",
"integrity": "sha512-45oQJA0bxna4O5TMwz55/TpgjX1YrAPOI/rb6kPgmdnemRZx/dB0rsx+Ku8jpDvqTxcE1C/qEbVHbS3h0hflag==", "integrity": "sha512-6WN7UFYvykr/U0Qgy4kz48iGPWILvYL34xXJxvDQeiRE018B7POspNRVtAZscWntEPZpFCx4hcz/XBT+erenfg==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/visitor-keys": "4.29.3", "@typescript-eslint/visitor-keys": "4.30.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"globby": "^11.0.3", "globby": "^11.0.3",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -2718,11 +2718,11 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.30.0.tgz",
"integrity": "sha512-MGGfJvXT4asUTeVs0Q2m+sY63UsfnA+C/FDgBKV3itLBmM9H0u+URcneePtkd0at1YELmZK6HSolCqM4Fzs6yA==", "integrity": "sha512-pNaaxDt/Ol/+JZwzP7MqWc8PJQTUhZwoee/PVlQ+iYoYhagccvoHnC9e4l+C/krQYYkENxznhVSDwClIbZVxRw==",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
}, },
"engines": { "engines": {
@ -9566,9 +9566,9 @@
"integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==" "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg=="
}, },
"@goauthentik/api": { "@goauthentik/api": {
"version": "2021.8.2-1629997023", "version": "2021.8.3-1630597235",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.8.2-1629997023.tgz", "resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.8.3-1630597235.tgz",
"integrity": "sha512-j80ZOgU+ZP40WD6PGJzxYmVKeGEcNJLjRqAQDvVmdfxA1++O6Ul1hYk02kSrDpQRbjdqmuO3u3a9s/HHKEvkrw==" "integrity": "sha512-W/AWgWE8WS8pWYPqy9JuzbzdYcTJ7aCjzgcLbOmD4roYTp1vP1znSZKx0zg9ZH7EBskFfDqm2eAueF1ktPC09g=="
}, },
"@humanwhocodes/config-array": { "@humanwhocodes/config-array": {
"version": "0.5.0", "version": "0.5.0",
@ -10022,13 +10022,13 @@
} }
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.12.0.tgz",
"integrity": "sha512-Qr2QRA0t5/S9QQqxzYKvM9W8prvmiWuldfwRX4hubovXzcXLgUi4WK0/H612wSbYZ4dNAEcQbtlxFWJNN4wxdg==", "integrity": "sha512-wsJi1NLOmfwtPNYxEC50dpDcVY7sdYckzwfqz1/zHrede1mtxpqSw+7iP4bHADOJXuF+ObYYTHND0v38GSXznQ==",
"requires": { "requires": {
"@sentry/core": "6.11.0", "@sentry/core": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10040,14 +10040,14 @@
} }
}, },
"@sentry/core": { "@sentry/core": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.12.0.tgz",
"integrity": "sha512-09TB+f3pqEq8LFahFWHO6I/4DxHo+NcS52OkbWMDqEi6oNZRD7PhPn3i14LfjsYVv3u3AESU8oxSEGbFrr2UjQ==", "integrity": "sha512-mU/zdjlzFHzdXDZCPZm8OeCw7c9xsbL49Mq0TrY0KJjLt4CJBkiq5SDTGfRsenBLgTedYhe5Z/J8Z+xVVq+MfQ==",
"requires": { "requires": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/minimal": "6.11.0", "@sentry/minimal": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10059,12 +10059,12 @@
} }
}, },
"@sentry/hub": { "@sentry/hub": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.12.0.tgz",
"integrity": "sha512-pT9hf+ZJfVFpoZopoC+yJmFNclr4NPqPcl2cgguqCHb69DklD1NxgBNWK8D6X05qjnNFDF991U6t1mxP9HrGuw==", "integrity": "sha512-yR/UQVU+ukr42bSYpeqvb989SowIXlKBanU0cqLFDmv5LPCnaQB8PGeXwJAwWhQgx44PARhmB82S6Xor8gYNxg==",
"requires": { "requires": {
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10076,12 +10076,12 @@
} }
}, },
"@sentry/minimal": { "@sentry/minimal": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.12.0.tgz",
"integrity": "sha512-XkZ7qrdlGp4IM/gjGxf1Q575yIbl5RvPbg+WFeekpo16Ufvzx37Mr8c2xsZaWosISVyE6eyFpooORjUlzy8EDw==", "integrity": "sha512-r3C54Q1KN+xIqUvcgX9DlcoWE7ezWvFk2pSu1Ojx9De81hVqR9u5T3sdSAP2Xma+um0zr6coOtDJG4WtYlOtsw==",
"requires": { "requires": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10093,14 +10093,14 @@
} }
}, },
"@sentry/tracing": { "@sentry/tracing": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.12.0.tgz",
"integrity": "sha512-9VA1/SY++WeoMQI4K6n/sYgIdRtCu9NLWqmGqu/5kbOtESYFgAt1DqSyqGCr00ZjQiC2s7tkDkTNZb38K6KytQ==", "integrity": "sha512-u10QHNknPBzbWSUUNMkvuH53sQd5NaBo6YdNPj4p5b7sE7445Sh0PwBpRbY3ZiUUiwyxV59fx9UQ4yVnPGxZQA==",
"requires": { "requires": {
"@sentry/hub": "6.11.0", "@sentry/hub": "6.12.0",
"@sentry/minimal": "6.11.0", "@sentry/minimal": "6.12.0",
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"@sentry/utils": "6.11.0", "@sentry/utils": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10112,16 +10112,16 @@
} }
}, },
"@sentry/types": { "@sentry/types": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.12.0.tgz",
"integrity": "sha512-gm5H9eZhL6bsIy/h3T+/Fzzz2vINhHhqd92CjHle3w7uXdTdFV98i2pDpErBGNTSNzbntqOMifYEB5ENtZAvcg==" "integrity": "sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA=="
}, },
"@sentry/utils": { "@sentry/utils": {
"version": "6.11.0", "version": "6.12.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.12.0.tgz",
"integrity": "sha512-IOvyFHcnbRQxa++jO+ZUzRvFHEJ1cZjrBIQaNVc0IYF0twUOB5PTP6joTcix38ldaLeapaPZ9LGfudbvYvxkdg==", "integrity": "sha512-oRHQ7TH5TSsJqoP9Gqq25Jvn9LKexXfAh/OoKwjMhYCGKGhqpDNUIZVgl9DWsGw5A5N5xnQyLOxDfyRV5RshdA==",
"requires": { "requires": {
"@sentry/types": "6.11.0", "@sentry/types": "6.12.0",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"dependencies": { "dependencies": {
@ -10292,12 +10292,12 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.30.0.tgz",
"integrity": "sha512-tBgfA3K/3TsZY46ROGvoRxQr1wBkclbVqRQep97MjVHJzcRBURRY3sNFqLk0/Xr//BY5hM9H2p/kp+6qim85SA==", "integrity": "sha512-NgAnqk55RQ/SD+tZFD9aPwNSeHmDHHe5rtUyhIq0ZeCWZEvo4DK9rYz7v9HDuQZFvn320Ot+AikaCKMFKLlD0g==",
"requires": { "requires": {
"@typescript-eslint/experimental-utils": "4.29.3", "@typescript-eslint/experimental-utils": "4.30.0",
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1", "functional-red-black-tree": "^1.0.1",
"regexpp": "^3.1.0", "regexpp": "^3.1.0",
@ -10316,50 +10316,50 @@
} }
}, },
"@typescript-eslint/experimental-utils": { "@typescript-eslint/experimental-utils": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.30.0.tgz",
"integrity": "sha512-ffIvbytTVWz+3keg+Sy94FG1QeOvmV9dP2YSdLFHw/ieLXWCa3U1TYu8IRCOpMv2/SPS8XqhM1+ou1YHsdzKrg==", "integrity": "sha512-K8RNIX9GnBsv5v4TjtwkKtqMSzYpjqAQg/oSphtxf3xxdt6T0owqnpojztjjTcatSteH3hLj3t/kklKx87NPqw==",
"requires": { "requires": {
"@types/json-schema": "^7.0.7", "@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/typescript-estree": "4.29.3", "@typescript-eslint/typescript-estree": "4.30.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0" "eslint-utils": "^3.0.0"
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.30.0.tgz",
"integrity": "sha512-jrHOV5g2u8ROghmspKoW7pN8T/qUzk0+DITun0MELptvngtMrwUJ1tv5zMI04CYVEUsSrN4jV7AKSv+I0y0EfQ==", "integrity": "sha512-HJ0XuluSZSxeboLU7Q2VQ6eLlCwXPBOGnA7CqgBnz2Db3JRQYyBDJgQnop6TZ+rsbSx5gEdWhw4rE4mDa1FnZg==",
"requires": { "requires": {
"@typescript-eslint/scope-manager": "4.29.3", "@typescript-eslint/scope-manager": "4.30.0",
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/typescript-estree": "4.29.3", "@typescript-eslint/typescript-estree": "4.30.0",
"debug": "^4.3.1" "debug": "^4.3.1"
} }
}, },
"@typescript-eslint/scope-manager": { "@typescript-eslint/scope-manager": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.30.0.tgz",
"integrity": "sha512-x+w8BLXO7iWPkG5mEy9bA1iFRnk36p/goVlYobVWHyDw69YmaH9q6eA+Fgl7kYHmFvWlebUTUfhtIg4zbbl8PA==", "integrity": "sha512-VJ/jAXovxNh7rIXCQbYhkyV2Y3Ac/0cVHP/FruTJSAUUm4Oacmn/nkN5zfWmWFEanN4ggP0vJSHOeajtHq3f8A==",
"requires": { "requires": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/visitor-keys": "4.29.3" "@typescript-eslint/visitor-keys": "4.30.0"
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.30.0.tgz",
"integrity": "sha512-s1eV1lKNgoIYLAl1JUba8NhULmf+jOmmeFO1G5MN/RBCyyzg4TIOfIOICVNC06lor+Xmy4FypIIhFiJXOknhIg==" "integrity": "sha512-YKldqbNU9K4WpTNwBqtAerQKLLW/X2A/j4yw92e3ZJYLx+BpKLeheyzoPfzIXHfM8BXfoleTdiYwpsvVPvHrDw=="
}, },
"@typescript-eslint/typescript-estree": { "@typescript-eslint/typescript-estree": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.30.0.tgz",
"integrity": "sha512-45oQJA0bxna4O5TMwz55/TpgjX1YrAPOI/rb6kPgmdnemRZx/dB0rsx+Ku8jpDvqTxcE1C/qEbVHbS3h0hflag==", "integrity": "sha512-6WN7UFYvykr/U0Qgy4kz48iGPWILvYL34xXJxvDQeiRE018B7POspNRVtAZscWntEPZpFCx4hcz/XBT+erenfg==",
"requires": { "requires": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"@typescript-eslint/visitor-keys": "4.29.3", "@typescript-eslint/visitor-keys": "4.30.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"globby": "^11.0.3", "globby": "^11.0.3",
"is-glob": "^4.0.1", "is-glob": "^4.0.1",
@ -10378,11 +10378,11 @@
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
"version": "4.29.3", "version": "4.30.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.3.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.30.0.tgz",
"integrity": "sha512-MGGfJvXT4asUTeVs0Q2m+sY63UsfnA+C/FDgBKV3itLBmM9H0u+URcneePtkd0at1YELmZK6HSolCqM4Fzs6yA==", "integrity": "sha512-pNaaxDt/Ol/+JZwzP7MqWc8PJQTUhZwoee/PVlQ+iYoYhagccvoHnC9e4l+C/krQYYkENxznhVSDwClIbZVxRw==",
"requires": { "requires": {
"@typescript-eslint/types": "4.29.3", "@typescript-eslint/types": "4.30.0",
"eslint-visitor-keys": "^2.0.0" "eslint-visitor-keys": "^2.0.0"
} }
}, },

View File

@ -46,7 +46,7 @@
"@babel/preset-env": "^7.15.0", "@babel/preset-env": "^7.15.0",
"@babel/preset-typescript": "^7.15.0", "@babel/preset-typescript": "^7.15.0",
"@fortawesome/fontawesome-free": "^5.15.4", "@fortawesome/fontawesome-free": "^5.15.4",
"@goauthentik/api": "^2021.8.2-1629997023", "@goauthentik/api": "^2021.8.3-1630597235",
"@lingui/cli": "^3.10.2", "@lingui/cli": "^3.10.2",
"@lingui/core": "^3.10.4", "@lingui/core": "^3.10.4",
"@lingui/macro": "^3.10.2", "@lingui/macro": "^3.10.2",
@ -56,13 +56,13 @@
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^3.0.0", "@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5", "@rollup/plugin-typescript": "^8.2.5",
"@sentry/browser": "^6.11.0", "@sentry/browser": "^6.12.0",
"@sentry/tracing": "^6.11.0", "@sentry/tracing": "^6.12.0",
"@types/chart.js": "^2.9.34", "@types/chart.js": "^2.9.34",
"@types/codemirror": "5.60.2", "@types/codemirror": "5.60.2",
"@types/grecaptcha": "^3.0.3", "@types/grecaptcha": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^4.29.3", "@typescript-eslint/eslint-plugin": "^4.30.0",
"@typescript-eslint/parser": "^4.29.3", "@typescript-eslint/parser": "^4.30.0",
"@webcomponents/webcomponentsjs": "^2.6.0", "@webcomponents/webcomponentsjs": "^2.6.0",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"base64-js": "^1.5.1", "base64-js": "^1.5.1",

View File

@ -2,6 +2,7 @@ import { Config, Configuration, CoreApi, CurrentTenant, Middleware, ResponseCont
import { getCookie } from "../utils"; import { getCookie } from "../utils";
import { APIMiddleware } from "../elements/notifications/APIDrawer"; import { APIMiddleware } from "../elements/notifications/APIDrawer";
import { MessageMiddleware } from "../elements/messages/Middleware"; import { MessageMiddleware } from "../elements/messages/Middleware";
import { VERSION } from "../constants";
export class LoggingMiddleware implements Middleware { export class LoggingMiddleware implements Middleware {
@ -49,7 +50,7 @@ export function tenant(): Promise<CurrentTenant> {
} }
export const DEFAULT_CONFIG = new Configuration({ export const DEFAULT_CONFIG = new Configuration({
basePath: "", basePath: "/api/v3",
headers: { headers: {
"X-CSRFToken": getCookie("authentik_csrf"), "X-CSRFToken": getCookie("authentik_csrf"),
}, },
@ -59,3 +60,5 @@ export const DEFAULT_CONFIG = new Configuration({
new LoggingMiddleware(), new LoggingMiddleware(),
], ],
}); });
console.debug(`authentik(early): version ${VERSION}`);

View File

@ -15,7 +15,7 @@ export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
Sentry.init({ Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8", dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
release: `authentik@${VERSION}`, release: `authentik@${VERSION}`,
tunnel: "/api/v2beta/sentry/", tunnel: "/api/v3/sentry/",
integrations: [ integrations: [
new Integrations.BrowserTracing({ new Integrations.BrowserTracing({
tracingOrigins: [window.location.host, "localhost"], tracingOrigins: [window.location.host, "localhost"],

View File

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

View File

@ -224,9 +224,15 @@ export class Form<T> extends LitElement {
throw ex; throw ex;
}) })
.catch((ex: Error) => { .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;
}
// error is local or not from rest_framework // error is local or not from rest_framework
showMessage({ showMessage({
message: ex.toString(), message: msg,
level: MessageLevel.error, level: MessageLevel.error,
}); });
// rethrow the error so the form doesn't close // rethrow the error so the form doesn't close

View File

@ -77,6 +77,7 @@ export class FlowExecutor extends LitElement implements StageHost {
document.title = tenant.brandingTitle || TITLE_DEFAULT; document.title = tenant.brandingTitle || TITLE_DEFAULT;
} }
}); });
this.requestUpdate();
} }
get challenge(): ChallengeTypes | undefined { get challenge(): ChallengeTypes | undefined {

View File

@ -180,7 +180,7 @@ export class AuthenticatorValidateStage
${this.selectedDeviceChallenge ${this.selectedDeviceChallenge
? "" ? ""
: html`<p class="pf-c-login__main-header-desc"> : html`<p class="pf-c-login__main-header-desc">
${t`Select an identification method.`} ${t`Select an authentication method.`}
</p>`} </p>`}
</header> </header>
${this.selectedDeviceChallenge ${this.selectedDeviceChallenge

View File

@ -3370,6 +3370,10 @@ msgstr "Request token URL"
msgid "Required" msgid "Required"
msgstr "Required" msgstr "Required"
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr "Required."
#: src/pages/user-settings/UserSelfForm.ts #: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.ts #: src/pages/users/ServiceAccountForm.ts
#: src/pages/users/UserForm.ts #: src/pages/users/UserForm.ts
@ -3524,13 +3528,17 @@ msgstr "Select a provider that this application should use. Alternatively, creat
msgid "Select all rows" msgid "Select all rows"
msgstr "Select all rows" msgstr "Select all rows"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an authentication method."
msgstr "Select an authentication method."
#: src/pages/stages/invitation/InvitationListLink.ts #: src/pages/stages/invitation/InvitationListLink.ts
msgid "Select an enrollment flow" msgid "Select an enrollment flow"
msgstr "Select an enrollment flow" msgstr "Select an enrollment flow"
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts #:
msgid "Select an identification method." #~ msgid "Select an identification method."
msgstr "Select an identification method." #~ msgstr "Select an identification method."
#: src/pages/users/GroupSelectModal.ts #: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to" msgid "Select groups to add user to"
@ -3757,9 +3765,13 @@ msgstr "Source(s)"
msgid "Sources" msgid "Sources"
msgstr "Sources" msgstr "Sources"
#:
#~ msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#~ msgstr "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#: src/pages/sources/SourcesListPage.ts #: src/pages/sources/SourcesListPage.ts
msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgstr "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
#: src/pages/flows/BoundStagesList.ts #: src/pages/flows/BoundStagesList.ts
#: src/pages/flows/StageBindingForm.ts #: src/pages/flows/StageBindingForm.ts

View File

@ -3362,6 +3362,10 @@ msgstr ""
msgid "Required" msgid "Required"
msgstr "" msgstr ""
#: src/flows/stages/prompt/PromptStage.ts
msgid "Required."
msgstr ""
#: src/pages/user-settings/UserSelfForm.ts #: src/pages/user-settings/UserSelfForm.ts
#: src/pages/users/ServiceAccountForm.ts #: src/pages/users/ServiceAccountForm.ts
#: src/pages/users/UserForm.ts #: src/pages/users/UserForm.ts
@ -3516,13 +3520,17 @@ msgstr ""
msgid "Select all rows" msgid "Select all rows"
msgstr "" msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
msgid "Select an authentication method."
msgstr ""
#: src/pages/stages/invitation/InvitationListLink.ts #: src/pages/stages/invitation/InvitationListLink.ts
msgid "Select an enrollment flow" msgid "Select an enrollment flow"
msgstr "" msgstr ""
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts #:
msgid "Select an identification method." #~ msgid "Select an identification method."
msgstr "" #~ msgstr ""
#: src/pages/users/GroupSelectModal.ts #: src/pages/users/GroupSelectModal.ts
msgid "Select groups to add user to" msgid "Select groups to add user to"
@ -3749,8 +3757,12 @@ msgstr ""
msgid "Sources" msgid "Sources"
msgstr "" msgstr ""
#:
#~ msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins"
#~ msgstr ""
#: src/pages/sources/SourcesListPage.ts #: src/pages/sources/SourcesListPage.ts
msgid "Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins" msgid "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves."
msgstr "" msgstr ""
#: src/pages/flows/BoundStagesList.ts #: src/pages/flows/BoundStagesList.ts

View File

@ -32,7 +32,6 @@ export class SystemStatusCard extends AdminStatusCard<System> {
}); });
} }
const timeDiff = value.serverTime.getTime() - (this.now || new Date()).getTime(); const timeDiff = value.serverTime.getTime() - (this.now || new Date()).getTime();
console.log(`authentik/: timediff ${timeDiff}`);
if (timeDiff > 5000 || timeDiff < -5000) { if (timeDiff > 5000 || timeDiff < -5000) {
this.header = t`Warning`; this.header = t`Warning`;
return Promise.resolve<AdminStatus>({ return Promise.resolve<AdminStatus>({

View File

@ -243,7 +243,7 @@ export class FlowForm extends ModelForm<Flow, string> {
<input <input
type="checkbox" type="checkbox"
class="pf-c-check__input" class="pf-c-check__input"
?checked=${first(this.instance?.compatibilityMode, true)} ?checked=${first(this.instance?.compatibilityMode, false)}
/> />
<label class="pf-c-check__label"> ${t`Compatibility mode`} </label> <label class="pf-c-check__label"> ${t`Compatibility mode`} </label>
</div> </div>

View File

@ -101,9 +101,7 @@ export class FlowListPage extends TablePage<Flow> {
slug: item.slug, slug: item.slug,
}) })
.then((link) => { .then((link) => {
window.location.assign( window.open(`${link.link}?next=/%23${window.location.href}`);
`${link.link}?next=/%23${window.location.href}`,
);
}); });
}} }}
> >

View File

@ -25,7 +25,7 @@ export class SourceListPage extends TablePage<Source> {
return t`Sources`; return t`Sources`;
} }
pageDescription(): string | undefined { pageDescription(): string | undefined {
return t`Sources of identities, which can either be synced into authentik's database, like LDAP, or can be used by users to authenticate and enroll themselves, like OAuth and social logins`; return t`Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves.`;
} }
pageIcon(): string { pageIcon(): string {
return "pf-icon pf-icon-middleware"; return "pf-icon pf-icon-middleware";

View File

@ -48,6 +48,9 @@ export class InvitationListLink extends LitElement {
designation: FlowsInstancesListDesignationEnum.Enrollment, designation: FlowsInstancesListDesignationEnum.Enrollment,
}) })
.then((flows) => { .then((flows) => {
if (!this.selectedFlow && flows.results.length > 0) {
this.selectedFlow = flows.results[0].slug;
}
return flows.results.map((flow) => { return flows.results.map((flow) => {
return html`<option return html`<option
value=${flow.slug} value=${flow.slug}

View File

@ -103,7 +103,7 @@ export class TokenListPage extends TablePage<Token> {
row(item: Token): TemplateResult[] { row(item: Token): TemplateResult[] {
return [ return [
html`${item.identifier}`, html`${item.identifier}`,
html`${item.userObj?.username}`, html`<a href="#/identity/users/${item.userObj?.pk}">${item.userObj?.username}</a>`,
html`${item.expiring ? t`Yes` : t`No`}`, html`${item.expiring ? t`Yes` : t`No`}`,
html`${item.expiring ? item.expires?.toLocaleString() : "-"}`, html`${item.expiring ? item.expires?.toLocaleString() : "-"}`,
html`${IntentToLabel(item.intent || IntentEnum.Api)}`, html`${IntentToLabel(item.intent || IntentEnum.Api)}`,

View File

@ -51,10 +51,14 @@ export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
slot="form" slot="form"
successMessage=${t`Successfully updated device.`} successMessage=${t`Successfully updated device.`}
.send=${(data: unknown) => { .send=${(data: unknown) => {
return new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnUpdate({ return new AuthenticatorsApi(DEFAULT_CONFIG)
id: device.pk || 0, .authenticatorsWebauthnUpdate({
webAuthnDeviceRequest: data as WebAuthnDevice, id: device.pk || 0,
}); webAuthnDeviceRequest: data as WebAuthnDevice,
})
.then(() => {
this.requestUpdate();
});
}} }}
> >
<form class="pf-c-form pf-m-horizontal"> <form class="pf-c-form pf-m-horizontal">

View File

@ -2,9 +2,9 @@
title: API title: API
--- ---
Starting with 2021.3.5, every authentik instance has a built-in API browser, which can be accessed at https://authentik.company/api/v2beta/. Starting with 2021.3.5, every authentik instance has a built-in API browser, which can be accessed at https://authentik.company/api/v3/.
To generate an API client, you can use the OpenAPI v3 schema at https://authentik.company/api/v2beta/schema/. To generate an API client, you can use the OpenAPI v3 schema at https://authentik.company/api/v3/schema/.
While testing, the API requests are authenticated by your browser session. While testing, the API requests are authenticated by your browser session.

View File

@ -10,13 +10,13 @@ However, any flow can be executed via an API from anywhere, in fact that is what
Because the flow executor stores its state in the HTTP Session, so you need to ensure cookies between flow executor requests are persisted. Because the flow executor stores its state in the HTTP Session, so you need to ensure cookies between flow executor requests are persisted.
::: :::
The main endpoint for flow execution is `/api/v2beta/flows/executor/:slug`. The main endpoint for flow execution is `/api/v3/flows/executor/:slug`.
This endpoint accepts a query parameter called `query`, in which the flow executor sends the full Query-string. This endpoint accepts a query parameter called `query`, in which the flow executor sends the full Query-string.
To initiate a new flow, execute a GET request. To initiate a new flow, execute a GET request.
## `GET /api/v2beta/flows/executor/test-flow/` ## `GET /api/v3/flows/executor/test-flow/`
Below is the response, for example for an Identification stage. Below is the response, for example for an Identification stage.
@ -45,7 +45,7 @@ Below is the response, for example for an Identification stage.
To respond to this challenge, send a response: To respond to this challenge, send a response:
## `POST /api/v2beta/flows/executor/test-flow/` ## `POST /api/v3/flows/executor/test-flow/`
With this body With this body
@ -63,7 +63,7 @@ Depending on the flow, you'll either get a 200 Response with another challenge,
Depending also on the stage, a response might take longer to be returned (especially with the Duo Authenticator validation). Depending also on the stage, a response might take longer to be returned (especially with the Duo Authenticator validation).
To see the data layout for every stage possible, see the [API Browser](https://goauthentik.io/api/#get-/api/v2beta/flows/executor/-flow_slug-/) To see the data layout for every stage possible, see the [API Browser](https://goauthentik.io/api/#get-/api/v3/flows/executor/-flow_slug-/)
## Result ## Result

View File

@ -0,0 +1,48 @@
---
title: Websocket API
---
authentik has two different WebSocket endpoints, one is used for web-based clients to get real-time updates, and the other is used for outposts to report their healthiness.
### Web `/ws/client/`
:::info
Authentication is done using the session, so make sure to send the `Cookie` header.
:::
All messages have a common field called `message_type` to discern the type of message.
#### `message` type:
This type is used when the backend has a notice to show to the user. A full payload looks like:
```json
{
"message_type": "message",
"level": "error" | "warning" | "success" | "info",
"tags": "",
"message": "a message",
}
```
### Outpost `/ws/outpost/<outpost-uuid>/`
:::info
Authentication is done via the `Authorization` header, same as the regular API. You must send a valid token with a `Bearer ` prefix.
:::
All messages have two fields, `instruction` and `args`. Instruction is any number from this list:
- `0`: ACK, simply acknowledges the previous message
- `1`: HELLO, used for monitoring and regularly sent by outposts
- `2`: TRIGGER_UPDATE, sent by authentik to trigger a reload of the configuration
Arguments for these messages vary, all though these common args are always sent:
- `args['uuid']`: A unique UUID generated on startup of an outpost, used to uniquely identify it.
These fields are only sent for HELLO instructions:
- `args['version']`: Version of the outpost
- `args['buildHash']`: Build hash of the outpost, when available

View File

@ -23,6 +23,21 @@ All of these variables can be set to values, but you can also use a URI-like for
- `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432 - `AUTHENTIK_POSTGRESQL__PORT`: Database port, defaults to 5432
- `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD` - `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
### PostgreSQL Backup Settings
Optionally enable automated database backups to S3 or S3-compatible storages.
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__ACCESS_KEY`: S3 Access Key
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__SECRET_KEY`: S3 Secret Key
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__BUCKET`: S3 Bucket
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__REGION`: S3 Region, defaults to `eu-central-1`
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__LOCATION`: Relative Location of the files to the bucket. Defaults to the root of the bucket.
To use an S3-compatible storage, set the following settings.
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__HOST`: URL to the Service, for example `https://play.min.io`
- `AUTHENTIK_POSTGRESQL__S3_BACKUP__INSECURE_SKIP_VERIFY`: Set to `true` to disable SSL Certificate verification.
## Redis Settings ## Redis Settings
- `AUTHENTIK_REDIS__HOST`: Hostname of your Redis Server - `AUTHENTIK_REDIS__HOST`: Hostname of your Redis Server

View File

@ -12,9 +12,9 @@ This installation method is for test-setups and small-scale productive setups.
## Preparation ## Preparation
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.8.3/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.8.4/docker-compose.yml). Place it in a directory of your choice.
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.8.3 >> .env` To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.8.4 >> .env`
If this is a fresh authentik install run the following commands to generate a password: If this is a fresh authentik install run the following commands to generate a password:

View File

@ -0,0 +1,43 @@
---
title: Embedded Outpost
---
Starting with 2021.8.1, authentik comes with an embedded outpost. This has been added to simplify deployment for users using the Proxy provider.
The embedded outpost is ran in the main `server` container, and is managed by authentik itself. The embedded outpost authenticates itself via the secret key.
You can access the embedded outpost on the same ports as authentik itself, 9000 and 9443.
The embedded outpost cannot be disabled, if it doesn't make sense to use in your deployment you can simply ignore it.
### Configuration
Since the outpost uses OAuth2, there is some configuration required.
On a fresh authentik install, your Outpost list will look like this:
![](./stock.png)
Click the edit button on the right of the colum, and set the value of `authentik_host` to the URL you want to login with.
Make sure to set it to full URL, only configuring a hostname or FQDN will not work.
### Routing
Routing is handled like this:
1. Paths starting with `/static`, `/media` and `/help` return packaged CSS/JS files, and user-uploaded media files.
2. Paths starting with `/akprox` are sent to the embedded outpost.
3. Any hosts configured in the providers assigned to the embedded outpost are sent to the outpost.
4. Everything remaining is sent to the authentik backend server.
### Differences
There are a few more differences between managed outposts and the embedded outpost, mainly due to the fact that authentik can't fully manage the containers.
1. (Docker-only) No automatic traefik labels are added to the server container.
When you deploy a managed outpost on docker, the container has several labels to automatically configure traefik. This is not done for the embedded outpost.
2. (Kubernetes-only) An additional service is created.
Since authentik does not know what the normal authentik Service is called, another one is created with a common set of labels that is always set.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -11,7 +11,7 @@ version: "3.5"
services: services:
authentik_proxy: authentik_proxy:
image: ghcr.io/goauthentik/proxy:2021.8.3 image: ghcr.io/goauthentik/proxy:2021.8.4
ports: ports:
- 4180:4180 - 4180:4180
- 4443:4443 - 4443:4443
@ -21,7 +21,7 @@ services:
AUTHENTIK_TOKEN: token-generated-by-authentik AUTHENTIK_TOKEN: token-generated-by-authentik
# Or, for the LDAP Outpost # Or, for the LDAP Outpost
authentik_proxy: authentik_proxy:
image: ghcr.io/goauthentik/ldap:2021.8.3 image: ghcr.io/goauthentik/ldap:2021.8.4
ports: ports:
- 389:3389 - 389:3389
environment: environment:

View File

@ -14,7 +14,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
name: authentik-outpost-api name: authentik-outpost-api
stringData: stringData:
authentik_host: "__AUTHENTIK_URL__" authentik_host: "__AUTHENTIK_URL__"
@ -29,7 +29,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
name: authentik-outpost name: authentik-outpost
spec: spec:
ports: ports:
@ -54,7 +54,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
name: authentik-outpost name: authentik-outpost
spec: spec:
selector: selector:
@ -62,14 +62,14 @@ spec:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
template: template:
metadata: metadata:
labels: labels:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
spec: spec:
containers: containers:
- env: - env:
@ -88,7 +88,7 @@ spec:
secretKeyRef: secretKeyRef:
key: authentik_host_insecure key: authentik_host_insecure
name: authentik-outpost-api name: authentik-outpost-api
image: ghcr.io/goauthentik/proxy:2021.8.3 image: ghcr.io/goauthentik/proxy:2021.8.4
name: proxy name: proxy
ports: ports:
- containerPort: 4180 - containerPort: 4180
@ -98,7 +98,7 @@ spec:
name: https name: https
protocol: TCP protocol: TCP
--- ---
apiVersion: extensions/v1beta1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
annotations: annotations:
@ -110,7 +110,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.8.3 app.kubernetes.io/version: 2021.8.4
name: authentik-outpost name: authentik-outpost
spec: spec:
rules: rules:
@ -118,7 +118,9 @@ spec:
http: http:
paths: paths:
- backend: - backend:
serviceName: authentik-outpost service:
servicePort: http name: authentik-outpost
port:
name: http
path: / path: /
``` ```

View File

@ -66,7 +66,7 @@ This includes the following:
Can be any of: Can be any of:
- `password`: Standard password login - `password`: Standard password login
- `app_password`: App passowrd (token) - `app_password`: App password (token)
Sets `context['auth_method_args']` to Sets `context['auth_method_args']` to
```json ```json

View File

@ -261,6 +261,12 @@ spec:
Add the following settings to your IngressRoute Add the following settings to your IngressRoute
:::warning
By default traefik does not allow cross-namespace references for middlewares:
See [here](https://doc.traefik.io/traefik/v2.4/providers/kubernetes-crd/#allowcrossnamespace) to enable it.
:::
```yaml ```yaml
spec: spec:
routes: routes:
@ -268,8 +274,10 @@ spec:
match: "Host(`*external host that you configured in authentik*`)" match: "Host(`*external host that you configured in authentik*`)"
middlewares: middlewares:
- name: authentik - name: authentik
namespace: authentik
priority: 10 priority: 10
services: # Unchanged services: # Unchanged
# This part is only required for single-app setups
- kind: Rule - kind: Rule
match: "Host(`*external host that you configured in authentik*`) && PathPrefix(`/akprox/`)" match: "Host(`*external host that you configured in authentik*`) && PathPrefix(`/akprox/`)"
priority: 15 priority: 15

View File

@ -10,7 +10,7 @@ Default fields are exposed through auto-generated Property Mappings, which are p
| SSO (Redirect binding) | `/application/saml/<application slug>/sso/binding/redirect/` | | SSO (Redirect binding) | `/application/saml/<application slug>/sso/binding/redirect/` |
| SSO (POST binding) | `/application/saml/<application slug>/sso/binding/post/` | | SSO (POST binding) | `/application/saml/<application slug>/sso/binding/post/` |
| IdP-initiated login | `/application/saml/<application slug>/sso/binding/init/` | | IdP-initiated login | `/application/saml/<application slug>/sso/binding/init/` |
| Metadata Download | `/api/v2beta/providers/saml/<provider uid>/metadata/?download/`| | Metadata Download | `/api/v3/providers/saml/<provider uid>/metadata/?download/`|
You can download the metadata through the Webinterface, this link might be handy if your software wants to download the metadata directly. You can download the metadata through the Webinterface, this link might be handy if your software wants to download the metadata directly.

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