Compare commits

..

1 Commits

Author SHA1 Message Date
aa80babfff release: 2023.4.0 2023-04-14 13:28:57 +03:00
145 changed files with 1243 additions and 2215 deletions

View File

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

View File

@ -6,4 +6,3 @@ dist/**
build/**
build_docs/**
Dockerfile
authentik/enterprise

View File

@ -1,9 +1,10 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
title: ''
labels: bug
assignees: ""
assignees: ''
---
**Describe the bug**
@ -11,7 +12,6 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@ -27,9 +27,8 @@ If applicable, add screenshots to help explain your problem.
Output of docker-compose logs or kubectl logs respectively
**Version and Deployment (please complete the following information):**
- authentik version: [e.g. 2021.8.5]
- Deployment: [e.g. docker-compose, helm]
- authentik version: [e.g. 2021.8.5]
- Deployment: [e.g. docker-compose, helm]
**Additional context**
Add any other context about the problem here.

View File

@ -1,9 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
title: ''
labels: enhancement
assignees: ""
assignees: ''
---
**Is your feature request related to a problem? Please describe.**

View File

@ -1,9 +1,10 @@
---
name: Question
about: Ask a question about a feature or specific configuration
title: ""
title: ''
labels: question
assignees: ""
assignees: ''
---
**Describe your question/**
@ -19,9 +20,8 @@ If applicable, add screenshots to help explain your problem.
Output of docker-compose logs or kubectl logs respectively
**Version and Deployment (please complete the following information):**
- authentik version: [e.g. 2021.8.5]
- Deployment: [e.g. docker-compose, helm]
- authentik version: [e.g. 2021.8.5]
- Deployment: [e.g. docker-compose, helm]
**Additional context**
Add any other context about the problem here.

View File

@ -1,5 +1,5 @@
name: "Comment usage instructions on PRs"
description: "Comment usage instructions on PRs"
name: 'Comment usage instructions on PRs'
description: 'Comment usage instructions on PRs'
inputs:
tag:
@ -17,7 +17,7 @@ runs:
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: "github-actions[bot]"
comment-author: 'github-actions[bot]'
body-includes: authentik PR Installation instructions
- name: Create or update comment
uses: peter-evans/create-or-update-comment@v2

View File

@ -1,5 +1,5 @@
name: "Prepare docker environment variables"
description: "Prepare docker environment variables"
name: 'Prepare docker environment variables'
description: 'Prepare docker environment variables'
outputs:
shouldBuild:
@ -51,14 +51,12 @@ runs:
version_family = ".".join(version.split(".")[:-1])
safe_branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
sha = os.environ["GITHUB_SHA"] if not "${{ github.event.pull_request.head.sha }}" else "${{ github.event.pull_request.head.sha }}"
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
print("branchName=%s" % branch_name, file=_output)
print("branchNameContainer=%s" % safe_branch_name, file=_output)
print("timestamp=%s" % int(time()), file=_output)
print("sha=%s" % sha, file=_output)
print("shortHash=%s" % sha[:7], file=_output)
print("sha=%s" % os.environ["GITHUB_SHA"], file=_output)
print("shortHash=%s" % os.environ["GITHUB_SHA"][:7], file=_output)
print("shouldBuild=%s" % should_build, file=_output)
print("version=%s" % version, file=_output)
print("versionFamily=%s" % version_family, file=_output)

View File

@ -1,5 +1,5 @@
name: "Setup authentik testing environment"
description: "Setup authentik testing environment"
name: 'Setup authentik testing environment'
description: 'Setup authentik testing environment'
inputs:
postgresql_tag:
@ -18,13 +18,13 @@ runs:
- name: Setup python and restore poetry
uses: actions/setup-python@v3
with:
python-version: "3.11"
cache: "poetry"
python-version: '3.11'
cache: 'poetry'
- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v3.1.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Setup dependencies
shell: bash

View File

@ -1,21 +1,23 @@
version: "3.7"
version: '3.7'
services:
postgresql:
image: docker.io/library/postgres:${PSQL_TAG:-12}
container_name: postgres
image: library/postgres:${PSQL_TAG:-12}
volumes:
- db-data:/var/lib/postgresql/data
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: authentik
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
POSTGRES_DB: authentik
ports:
- 5432:5432
- 5432:5432
restart: always
redis:
image: docker.io/library/redis
container_name: redis
image: library/redis
ports:
- 6379:6379
- 6379:6379
restart: always
volumes:

120
.github/dependabot.yml vendored
View File

@ -1,62 +1,62 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "ci:"
- package-ecosystem: gomod
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"
- package-ecosystem: npm
directory: "/web"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "web:"
- package-ecosystem: npm
directory: "/website"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "website:"
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"
- package-ecosystem: docker
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "ci:"
- package-ecosystem: gomod
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"
- package-ecosystem: npm
directory: "/web"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "web:"
- package-ecosystem: npm
directory: "/website"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "website:"
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"
- package-ecosystem: docker
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
reviewers:
- "@goauthentik/core"
commit-message:
prefix: "core:"

View File

@ -5,20 +5,15 @@ Please check the [Contributing guidelines](https://github.com/goauthentik/authen
-->
# Details
- **Does this resolve an issue?**
Resolves #
* **Does this resolve an issue?**
Resolves #
## Changes
### New Features
- Adds feature which does x, y, and z.
* Adds feature which does x, y, and z.
### Breaking Changes
- Adds breaking change which causes \<issue\>.
* Adds breaking change which causes \<issue\>.
## Additional
Any further notes or comments you want to make.

View File

@ -6,11 +6,11 @@ git:
source_language: en
source_file: web/src/locales/en.po
# path expression to translation files, must contain <lang> placeholder
translation_files_expression: "web/src/locales/<lang>.po"
translation_files_expression: 'web/src/locales/<lang>.po'
- filter_type: file
# all supported i18n types: https://docs.transifex.com/formats
file_format: PO
source_language: en
source_file: locale/en/LC_MESSAGES/django.po
# path expression to translation files, must contain <lang> placeholder
translation_files_expression: "locale/<lang>/LC_MESSAGES/django.po"
translation_files_expression: 'locale/<lang>/LC_MESSAGES/django.po'

View File

@ -23,14 +23,13 @@ jobs:
fail-fast: false
matrix:
job:
- bandit
- black
- codespell
- isort
- pending-migrations
- pylint
- black
- isort
- bandit
- pyright
- ruff
- pending-migrations
- codespell
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -61,7 +60,7 @@ jobs:
cp authentik/lib/default.yml local.env.yml
cp -R .github ..
cp -R scripts ..
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))
git checkout $(git describe --abbrev=0 --match 'version/*')
rm -rf .github/ scripts/
mv ../.github ../scripts .
- name: Setup authentik env (ensure stable deps are installed)
@ -187,8 +186,6 @@ jobs:
timeout-minutes: 120
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Set up Docker Buildx
@ -214,7 +211,6 @@ jobs:
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
tags: |
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.sha }}
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.shortHash }}
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
@ -231,8 +227,6 @@ jobs:
timeout-minutes: 120
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Set up Docker Buildx
@ -258,7 +252,6 @@ jobs:
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
tags: |
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-arm64
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.sha }}-arm64
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.shortHash }}-arm64
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}

View File

@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
go-version: "^1.17"
- name: Prepare and generate API
run: |
# Create folder structure for go embeds
@ -30,14 +30,13 @@ jobs:
uses: golangci/golangci-lint-action@v3
with:
args: --timeout 5000s
skip-pkg-cache: true
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
go-version: "^1.17"
- name: Generate API
run: make gen-client-go
- name: Go unittests
@ -61,11 +60,11 @@ jobs:
- proxy
- ldap
- radius
arch:
- "linux/amd64"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Set up Docker Buildx
@ -95,7 +94,7 @@ jobs:
build-args: |
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
platforms: linux/amd64,linux/arm64
platforms: ${{ matrix.arch }}
context: .
build-binary:
timeout-minutes: 120
@ -113,14 +112,12 @@ jobs:
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
go-version: "^1.17"
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
node-version: "18"
cache: "npm"
cache-dependency-path: web/package-lock.json
- name: Generate API
@ -136,3 +133,7 @@ jobs:
export GOOS=${{ matrix.goos }}
export GOARCH=${{ matrix.goarch }}
go build -tags=outpost_static_embed -v -o ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }} ./cmd/${{ matrix.type }}
- uses: actions/upload-artifact@v3
with:
name: authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
path: ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}

View File

@ -17,8 +17,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci
@ -33,8 +33,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci
@ -49,8 +49,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci
@ -65,8 +65,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: |
@ -97,8 +97,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- working-directory: web/
run: npm ci

View File

@ -17,8 +17,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: website/package-lock.json
- working-directory: website/
run: npm ci
@ -31,8 +31,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: website/package-lock.json
- working-directory: website/
run: npm ci
@ -52,8 +52,8 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: website/package-lock.json
- working-directory: website/
run: npm ci

View File

@ -2,11 +2,12 @@ name: "CodeQL"
on:
push:
branches: [main, "*", next, version*]
branches: [ main, '*', next, version* ]
pull_request:
branches: [main]
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: "30 6 * * 5"
- cron: '30 6 * * 5'
jobs:
analyze:
@ -20,17 +21,40 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ["go", "javascript", "python"]
language: [ 'go', 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup authentik env
uses: ./.github/actions/setup
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -2,7 +2,7 @@ name: ghcr-retention
on:
schedule:
- cron: "0 0 * * *" # every day at midnight
- cron: '0 0 * * *' # every day at midnight
workflow_dispatch:
jobs:

View File

@ -57,7 +57,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
go-version: "^1.17"
- name: Set up QEMU
uses: docker/setup-qemu-action@v2.1.0
- name: Set up Docker Buildx
@ -107,11 +107,11 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
go-version: "^1.17"
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
cache: "npm"
node-version: '18'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Build web
working-directory: web/
@ -173,5 +173,5 @@ jobs:
SENTRY_PROJECT: authentik
with:
version: authentik@${{ steps.ev.outputs.version }}
sourcemaps: "./web/dist"
url_prefix: "~/static/dist"
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -3,7 +3,7 @@ name: authentik-on-tag
on:
push:
tags:
- "version/*"
- 'version/*'
jobs:
build:

View File

@ -1,12 +1,12 @@
name: authentik-backend-translate-compile
on:
push:
branches: [main]
branches: [ main ]
paths:
- "/locale/"
- '/locale/'
pull_request:
paths:
- "/locale/"
- '/locale/'
workflow_dispatch:
env:

View File

@ -1,9 +1,9 @@
name: authentik-web-api-publish
on:
push:
branches: [main]
branches: [ main ]
paths:
- "schema.yml"
- 'schema.yml'
workflow_dispatch:
jobs:
build:
@ -14,8 +14,8 @@ jobs:
token: ${{ secrets.BOT_GITHUB_TOKEN }}
- uses: actions/setup-node@v3.6.0
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
node-version: '18'
registry-url: 'https://registry.npmjs.org'
- name: Generate API Client
run: make gen-client-ts
- name: Publish package

View File

@ -1,5 +1,5 @@
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/node:20 as website-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:18 as website-builder
COPY ./website /work/website/
COPY ./blueprints /work/blueprints/
@ -10,7 +10,7 @@ WORKDIR /work/website
RUN npm ci && npm run build-docs-only
# Stage 2: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/node:20 as web-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:18 as web-builder
COPY ./web /work/web/
COPY ./website /work/website/
@ -83,9 +83,7 @@ RUN apt-get update && \
# Required for runtime
apt-get install -y --no-install-recommends libxmlsec1-openssl libmaxminddb0 && \
# Required for bootstrap & healtcheck
apt-get install -y --no-install-recommends runit && \
# Required for outposts
apt-get install -y --no-install-recommends openssh-client && \
apt-get install -y --no-install-recommends curl runit && \
pip install --no-cache-dir -r /requirements.txt && \
apt-get remove --purge -y build-essential pkg-config libxmlsec1-dev && \
apt-get autoremove --purge -y && \
@ -93,9 +91,8 @@ RUN apt-get update && \
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \
mkdir -p /certs /media /blueprints && \
chown authentik:authentik /certs /media && \
chmod g+w /etc/ssh/ssh_config.d/ && \
chgrp authentik /etc/ssh/ssh_config.d/
mkdir -p /authentik/.ssh && \
chown authentik:authentik /certs /media /authentik/.ssh
COPY ./authentik/ /authentik
COPY ./pyproject.toml /

View File

@ -1,11 +1,6 @@
Copyright (c) 2023 Jens Langhammer
MIT License
Portions of this software are licensed as follows:
* All content residing under the "website/" directory of this repository is licensed under "Creative Commons: CC BY-SA 4.0 license".
* All content that resides under the "authentik/enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "authentik/enterprise/LICENSE".
* All client-side JavaScript (when served directly or after being compiled, arranged, augmented, or combined), is licensed under the "MIT Expat" license.
* All third party components incorporated into the authentik are licensed under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined below.
Copyright (c) 2022 Jens Langhammer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -3,7 +3,6 @@ PWD = $(shell pwd)
UID = $(shell id -u)
GID = $(shell id -g)
NPM_VERSION = $(shell python -m scripts.npm_version)
PY_SOURCES = authentik tests scripts lifecycle
CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
-I .github/codespell-words.txt \
@ -39,14 +38,13 @@ test:
coverage report
lint-fix:
isort authentik $(PY_SOURCES)
black authentik $(PY_SOURCES)
ruff authentik $(PY_SOURCES)
isort authentik tests scripts lifecycle
black authentik tests scripts lifecycle
codespell -w $(CODESPELL_ARGS)
lint:
pylint $(PY_SOURCES)
bandit -r $(PY_SOURCES) -x node_modules
pylint authentik tests lifecycle
bandit -r authentik tests lifecycle -x node_modules
golangci-lint run -v
migrate:
@ -173,6 +171,7 @@ website-watch:
# These targets are use by GitHub actions to allow usage of matrix
# which makes the YAML File a lot smaller
PY_SOURCES=authentik tests lifecycle
ci--meta-debug:
python -V
node --version
@ -183,9 +182,6 @@ ci-pylint: ci--meta-debug
ci-black: ci--meta-debug
black --check $(PY_SOURCES)
ci-ruff: ci--meta-debug
ruff check $(PY_SOURCES)
ci-codespell: ci--meta-debug
codespell $(CODESPELL_ARGS) -s

View File

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

View File

@ -29,7 +29,6 @@ class Capabilities(models.TextChoices):
CAN_GEO_IP = "can_geo_ip"
CAN_IMPERSONATE = "can_impersonate"
CAN_DEBUG = "can_debug"
IS_ENTERPRISE = "is_enterprise"
class ErrorReportingConfigSerializer(PassiveSerializer):
@ -71,8 +70,6 @@ class ConfigView(APIView):
caps.append(Capabilities.CAN_IMPERSONATE)
if settings.DEBUG: # pragma: no cover
caps.append(Capabilities.CAN_DEBUG)
if "authentik.enterprise" in settings.INSTALLED_APPS:
caps.append(Capabilities.IS_ENTERPRISE)
return caps
def get_config(self) -> ConfigSerializer:

View File

@ -6,6 +6,7 @@ from pathlib import Path
import django.contrib.postgres.fields
from dacite.core import from_dict
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from yaml import load
@ -14,7 +15,7 @@ from authentik.blueprints.v1.labels import LABEL_AUTHENTIK_SYSTEM
from authentik.lib.config import CONFIG
def check_blueprint_v1_file(BlueprintInstance: type, path: Path):
def check_blueprint_v1_file(BlueprintInstance: type["BlueprintInstance"], path: Path):
"""Check if blueprint should be imported"""
from authentik.blueprints.models import BlueprintInstanceStatus
from authentik.blueprints.v1.common import BlueprintLoader, BlueprintMetadata

View File

@ -2,6 +2,8 @@
from django.db import migrations
from authentik.lib.generators import generate_id
class Migration(migrations.Migration):
dependencies = [

View File

@ -1,45 +0,0 @@
The authentik Enterprise Edition (EE) license (the “EE License”)
Copyright (c) 2022-present Authentik Security Inc.
With regard to the authentik Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Authentik Subscription Terms of Service, available
at https://goauthentik.io/legal/terms (the "EE Terms"), or other
agreement governing the use of the Software, as agreed by you and authentik Security Inc,
and otherwise have a valid authentik Enterprise Edition subscription for the
correct number of user seats. Subject to the foregoing sentence, you are free to
modify this Software and publish patches to the Software. You agree that Authentik
Security Inc. and/or its licensors (as applicable) retain all right, title and interest
in and to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid authentik Enterprise Edition subscription for the correct
number of user seats. Notwithstanding the foregoing, you may copy and modify
the Software for development and testing purposes, without requiring a
subscription. You agree that Authentik Security Inc. and/or its
licensors (as applicable) retain all right, title and interest in
and to all such modifications. You are not granted any other rights
beyond what is expressly stated herein. Subject to the
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
This EE License applies only to the part of this Software that is not
distributed as part of authentik Open Source (OSS). Any part of this Software
distributed as part of authentik OSS or is served client-side as an image, font,
cascading stylesheet (CSS), file which produces or is compiled, arranged,
augmented, or combined into client-side JavaScript, in whole or in part, is
copyrighted under the MIT license. The full text of this EE License shall
be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the authentik Software, those
components are licensed under the original license provided by the owner of the
applicable component.

View File

@ -1,11 +0,0 @@
"""Enterprise app config"""
from authentik.blueprints.apps import ManagedAppConfig
class AuthentikEnterpriseConfig(ManagedAppConfig):
"""Enterprise app config"""
name = "authentik.enterprise"
label = "authentik_enterprise"
verbose_name = "authentik Enterprise"
default = True

View File

@ -1 +0,0 @@
"""Enterprise additional settings"""

View File

@ -11,6 +11,7 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.events.models
import authentik.lib.models
from authentik.events.models import EventAction, NotificationSeverity, TransportMode
from authentik.lib.migrations import progress_bar

View File

@ -57,6 +57,10 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
LOGGER.debug("e(trigger): attempting to prevent infinite loop", trigger=trigger)
return
if not trigger.group:
LOGGER.debug("e(trigger): trigger has no group", trigger=trigger)
return
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
try:
user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
@ -73,10 +77,6 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
if not result.passing:
return
if not trigger.group:
LOGGER.debug("e(trigger): trigger has no group", trigger=trigger)
return
LOGGER.debug("e(trigger): event trigger matched", trigger=trigger)
# Create the notification objects
for transport in trigger.transports.all():

View File

@ -67,7 +67,7 @@ def sentry_init(**sentry_init_kwargs):
ArgvIntegration(),
StdlibIntegration(),
DjangoIntegration(transaction_style="function_name"),
CeleryIntegration(),
CeleryIntegration(monitor_beat_tasks=True),
RedisIntegration(),
ThreadingIntegration(propagate_hub=True),
SocketIntegration(),

View File

@ -1,5 +1,4 @@
"""Docker controller"""
from subprocess import SubprocessError # nosec
from time import sleep
from typing import Optional
from urllib.parse import urlparse
@ -10,9 +9,11 @@ from docker import DockerClient as UpstreamDockerClient
from docker.errors import DockerException, NotFound
from docker.models.containers import Container
from docker.utils.utils import kwargs_from_env
from paramiko.ssh_exception import SSHException
from structlog.stdlib import get_logger
from yaml import safe_dump
from authentik import __version__
from authentik.outposts.apps import MANAGED_OUTPOST
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException
@ -58,9 +59,8 @@ class DockerClient(UpstreamDockerClient, BaseClient):
super().__init__(
base_url=connection.url,
tls=tls_config,
use_ssh_client=True,
)
except SubprocessError as exc:
except SSHException as exc:
if self.ssh:
self.ssh.cleanup()
raise ServiceConnectionInvalid(exc) from exc

View File

@ -22,7 +22,7 @@ from kubernetes.client import (
V1SecurityContext,
)
from authentik import get_full_version
from authentik import __version__, get_full_version
from authentik.outposts.controllers.base import FIELD_MANAGER
from authentik.outposts.controllers.k8s.base import KubernetesObjectReconciler
from authentik.outposts.controllers.k8s.triggers import NeedsUpdate

View File

@ -7,7 +7,8 @@ from docker.errors import DockerException
from authentik.crypto.models import CertificateKeyPair
SSH_CONFIG_DIR = Path("/etc/ssh/ssh_config.d/")
HEADER = "### Managed by authentik"
FOOTER = "### End Managed by authentik"
def opener(path, flags):
@ -27,54 +28,70 @@ class DockerInlineSSH:
key_path: str
config_path: Path
header: str
def __init__(self, host: str, keypair: CertificateKeyPair) -> None:
self.host = host
self.keypair = keypair
self.config_path = SSH_CONFIG_DIR / Path(self.host + ".conf")
with open(self.config_path, "w", encoding="utf-8") as _config:
if not _config.writable():
# SSH Config file already exists and there's no header from us, meaning that it's
# been externally mapped into the container for more complex configs
raise SSHManagedExternallyException(
"SSH Config exists and does not contain authentik header"
)
self.config_path = Path("~/.ssh/config").expanduser()
if self.config_path.exists() and HEADER not in self.config_path.read_text(encoding="utf-8"):
# SSH Config file already exists and there's no header from us, meaning that it's
# been externally mapped into the container for more complex configs
raise SSHManagedExternallyException(
"SSH Config exists and does not contain authentik header"
)
if not self.keypair:
raise DockerException("keypair must be set for SSH connections")
self.header = f"{HEADER} - {self.host}\n"
def write_config(self, key_path: str):
def write_config(self, key_path: str) -> bool:
"""Update the local user's ssh config file"""
with open(self.config_path, "w", encoding="utf-8") as ssh_config:
with open(self.config_path, "a+", encoding="utf-8") as ssh_config:
if self.header in ssh_config.readlines():
return False
ssh_config.writelines(
[
self.header,
f"Host {self.host}\n",
f" IdentityFile {str(key_path)}\n",
f" IdentityFile {key_path}\n",
" StrictHostKeyChecking No\n",
" UserKnownHostsFile /dev/null\n",
f"{FOOTER}\n",
"\n",
]
)
return True
def write_key(self) -> Path:
def write_key(self):
"""Write keypair's private key to a temporary file"""
path = Path(gettempdir(), f"{self.keypair.pk}_private.pem")
with open(path, "w", encoding="utf8", opener=opener) as _file:
_file.write(self.keypair.key_data)
return path
return str(path)
def write(self):
"""Write keyfile and update ssh config"""
self.key_path = self.write_key()
try:
self.write_config(self.key_path)
except OSError:
was_written = self.write_config(self.key_path)
if not was_written:
self.cleanup()
def cleanup(self):
"""Cleanup when we're done"""
try:
os.unlink(self.key_path)
os.unlink(self.config_path)
with open(self.config_path, "r", encoding="utf-8") as ssh_config:
start = 0
end = 0
lines = ssh_config.readlines()
for idx, line in enumerate(lines):
if line == self.header:
start = idx
if start != 0 and line == f"{FOOTER}\n":
end = idx
with open(self.config_path, "w+", encoding="utf-8") as ssh_config:
lines = lines[:start] + lines[end + 2 :]
ssh_config.writelines(lines)
except OSError:
# If we fail deleting a file it doesn't matter that much
# since we're just in a container

View File

@ -59,7 +59,6 @@ class TestPasswordPolicyFlow(FlowTestCase):
"label": "PASSWORD_LABEL",
"order": 0,
"placeholder": "PASSWORD_PLACEHOLDER",
"initial_value": "",
"required": True,
"type": "password",
"sub_text": "",

View File

@ -1,8 +1,10 @@
# Generated by Django 3.1 on 2020-08-18 15:59
import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.core.models
import authentik.lib.generators

View File

@ -68,7 +68,7 @@ from authentik.stages.consent.stage import (
LOGGER = get_logger()
PLAN_CONTEXT_PARAMS = "goauthentik.io/providers/oauth2/params"
PLAN_CONTEXT_PARAMS = "params"
SESSION_KEY_LAST_LOGIN_UID = "authentik/providers/oauth2/last_login_uid"
ALLOWED_PROMPT_PARAMS = {PROMPT_NONE, PROMPT_CONSENT, PROMPT_LOGIN}

View File

@ -8,7 +8,7 @@ from authentik.flows.stage import ChallengeStageView
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.providers.oauth2.models import DeviceToken
PLAN_CONTEXT_DEVICE = "goauthentik.io/providers/oauth2/device"
PLAN_CONTEXT_DEVICE = "device"
class OAuthDeviceCodeFinishChallenge(Challenge):

View File

@ -29,6 +29,9 @@ from authentik.providers.oauth2.utils import cors_allow
LOGGER = get_logger()
PLAN_CONTEXT_PARAMS = "params"
PLAN_CONTEXT_SCOPES = "scopes"
class ProviderInfoView(View):
"""OpenID-compliant Provider Info"""

View File

@ -492,12 +492,3 @@ if DEBUG:
INSTALLED_APPS.append("authentik.core")
CONFIG.log("info", "Booting authentik", version=__version__)
# Attempt to load enterprise app, if available
try:
importlib.import_module("authentik.enterprise.apps")
CONFIG.log("info", "Enabled authentik enterprise")
INSTALLED_APPS.append("authentik.enterprise")
_update_settings("authentik.enterprise.settings")
except ImportError:
pass

View File

@ -3,6 +3,8 @@
import django.contrib.postgres.fields
from django.db import migrations, models
import authentik.lib.generators
class Migration(migrations.Migration):
dependencies = [

View File

@ -40,7 +40,11 @@ from authentik.sources.saml.models import SAMLBindingTypes, SAMLSource
from authentik.sources.saml.processors.metadata import MetadataProcessor
from authentik.sources.saml.processors.request import RequestProcessor
from authentik.sources.saml.processors.response import ResponseProcessor
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_HEADER, ConsentStageView
from authentik.stages.consent.stage import (
PLAN_CONTEXT_CONSENT_HEADER,
PLAN_CONTEXT_CONSENT_TITLE,
ConsentStageView,
)
LOGGER = get_logger()
@ -124,6 +128,7 @@ class InitiateView(View):
injected_stages = []
plan_kwargs = {
PLAN_CONTEXT_TITLE: f"Redirecting to {source.name}...",
PLAN_CONTEXT_CONSENT_TITLE: f"Redirecting to {source.name}...",
PLAN_CONTEXT_ATTRS: {
"SAMLRequest": saml_request,
"RelayState": relay_state,

View File

@ -10,6 +10,7 @@ from duo_client.admin import Admin
from duo_client.auth import Auth
from rest_framework.serializers import BaseSerializer, Serializer
from authentik import __version__
from authentik.core.types import UserSettingSerializer
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
from authentik.lib.models import SerializerModel

View File

@ -33,6 +33,7 @@ from authentik.stages.authenticator_validate.models import AuthenticatorValidate
from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice
from authentik.stages.authenticator_webauthn.stage import SESSION_KEY_WEBAUTHN_CHALLENGE
from authentik.stages.authenticator_webauthn.utils import get_origin, get_rp_id
from authentik.stages.consent.stage import PLAN_CONTEXT_CONSENT_TITLE
LOGGER = get_logger()
@ -174,6 +175,8 @@ def validate_challenge_duo(device_pk: int, stage_view: StageView, user: User) ->
pushinfo = {
__("Domain"): stage_view.request.get_host(),
}
if PLAN_CONTEXT_CONSENT_TITLE in stage_view.executor.plan.context:
pushinfo[__("Title")] = stage_view.executor.plan.context[PLAN_CONTEXT_CONSENT_TITLE]
if SESSION_KEY_APPLICATION_PRE in stage_view.request.session:
pushinfo[__("Application")] = stage_view.request.session.get(
SESSION_KEY_APPLICATION_PRE, Application()

View File

@ -14,6 +14,7 @@ from rest_framework.serializers import ValidationError
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import User
from authentik.events.models import Event, EventAction
from authentik.events.utils import cleanse_dict, sanitize_dict
from authentik.flows.challenge import ChallengeResponse, ChallengeTypes, WithUserInfoChallenge
from authentik.flows.exceptions import FlowSkipStageException
from authentik.flows.models import FlowDesignation, NotConfiguredAction, Stage
@ -381,9 +382,13 @@ class AuthenticatorValidateStageView(ChallengeStageView):
self.logger.debug("Set user from user-less flow", user=webauthn_device.user)
self.executor.plan.context[PLAN_CONTEXT_PENDING_USER] = webauthn_device.user
self.executor.plan.context[PLAN_CONTEXT_METHOD] = "auth_webauthn_pwl"
self.executor.plan.context[PLAN_CONTEXT_METHOD_ARGS] = {
"device": webauthn_device,
}
self.executor.plan.context[PLAN_CONTEXT_METHOD_ARGS] = cleanse_dict(
sanitize_dict(
{
"device": webauthn_device,
}
)
)
return self.set_valid_mfa_cookie(response.device)
def cleanup(self):

View File

@ -19,6 +19,7 @@ from authentik.lib.utils.time import timedelta_from_string
from authentik.stages.consent.models import ConsentMode, ConsentStage, UserConsent
PLAN_CONTEXT_CONSENT = "consent"
PLAN_CONTEXT_CONSENT_TITLE = "consent_title"
PLAN_CONTEXT_CONSENT_HEADER = "consent_header"
PLAN_CONTEXT_CONSENT_PERMISSIONS = "consent_permissions"
PLAN_CONTEXT_CONSENT_EXTRA_PERMISSIONS = "consent_additional_permissions"
@ -58,6 +59,8 @@ class ConsentStageView(ChallengeStageView):
),
"token": token,
}
if PLAN_CONTEXT_CONSENT_TITLE in self.executor.plan.context:
data["title"] = self.executor.plan.context[PLAN_CONTEXT_CONSENT_TITLE]
if PLAN_CONTEXT_CONSENT_HEADER in self.executor.plan.context:
data["header_text"] = self.executor.plan.context[PLAN_CONTEXT_CONSENT_HEADER]
challenge = ConsentChallenge(data=data)

View File

@ -4,7 +4,7 @@ from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from authentik.stages.password import BACKEND_APP_PASSWORD
from authentik.stages.password import BACKEND_APP_PASSWORD, BACKEND_INBUILT
def update_default_backends(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):

View File

@ -1,6 +1,7 @@
# Generated by Django 3.2.6 on 2021-08-23 14:34
import django.contrib.postgres.fields
from django.apps.registry import Apps
from django.db import migrations
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from authentik.stages.password import BACKEND_INBUILT

View File

@ -57,12 +57,10 @@ class PromptSerializer(ModelSerializer):
"type",
"required",
"placeholder",
"initial_value",
"order",
"promptstage_set",
"sub_text",
"placeholder_expression",
"initial_value_expression",
]

View File

@ -1,53 +0,0 @@
# Generated by Django 4.1.7 on 2023-03-24 17:32
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_placeholder_expressions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
from authentik.stages.prompt.models import CHOICE_FIELDS
db_alias = schema_editor.connection.alias
Prompt = apps.get_model("authentik_stages_prompt", "prompt")
for prompt in Prompt.objects.using(db_alias).all():
if not prompt.placeholder_expression or prompt.type in CHOICE_FIELDS:
continue
prompt.initial_value = prompt.placeholder
prompt.initial_value_expression = True
prompt.placeholder = ""
prompt.placeholder_expression = False
prompt.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_prompt", "0010_alter_prompt_placeholder_alter_prompt_type"),
]
operations = [
migrations.AddField(
model_name="prompt",
name="initial_value",
field=models.TextField(
blank=True,
help_text="Optionally pre-fill the input with an initial value. When creating a fixed choice field, enable interpreting as expression and return a list to return multiple default choices.",
),
),
migrations.AddField(
model_name="prompt",
name="initial_value_expression",
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name="prompt",
name="placeholder",
field=models.TextField(
blank=True,
help_text="Optionally provide a short hint that describes the expected input value. When creating a fixed choice field, enable interpreting as expression and return a list to return multiple choices.",
),
),
migrations.RunPython(code=migrate_placeholder_expressions),
]

View File

@ -29,8 +29,6 @@ from authentik.flows.models import Stage
from authentik.lib.models import SerializerModel
from authentik.policies.models import Policy
CHOICES_CONTEXT_SUFFIX = "__choices"
LOGGER = get_logger()
@ -121,25 +119,15 @@ class Prompt(SerializerModel):
placeholder = models.TextField(
blank=True,
help_text=_(
"Optionally provide a short hint that describes the expected input value. "
"When creating a fixed choice field, enable interpreting as "
"When creating a Radio Button Group or Dropdown, enable interpreting as "
"expression and return a list to return multiple choices."
),
)
initial_value = models.TextField(
blank=True,
help_text=_(
"Optionally pre-fill the input with an initial value. "
"When creating a fixed choice field, enable interpreting as "
"expression and return a list to return multiple default choices."
),
)
sub_text = models.TextField(blank=True, default="")
order = models.IntegerField(default=0)
placeholder_expression = models.BooleanField(default=False)
initial_value_expression = models.BooleanField(default=False)
@property
def serializer(self) -> Type[BaseSerializer]:
@ -160,8 +148,8 @@ class Prompt(SerializerModel):
raw_choices = self.placeholder
if self.field_key + CHOICES_CONTEXT_SUFFIX in prompt_context:
raw_choices = prompt_context[self.field_key + CHOICES_CONTEXT_SUFFIX]
if self.field_key in prompt_context:
raw_choices = prompt_context[self.field_key]
elif self.placeholder_expression:
evaluator = PropertyMappingEvaluator(
self, user, request, prompt_context=prompt_context, dry_run=dry_run
@ -196,9 +184,16 @@ class Prompt(SerializerModel):
) -> str:
"""Get fully interpolated placeholder"""
if self.type in CHOICE_FIELDS:
# Choice fields use the placeholder to define all valid choices.
# Therefore their actual placeholder is always blank
return ""
# Make sure to return a valid choice as placeholder
choices = self.get_choices(prompt_context, user, request, dry_run=dry_run)
if not choices:
return ""
return choices[0]
if self.field_key in prompt_context:
# We don't want to parse this as an expression since a user will
# be able to control the input
return prompt_context[self.field_key]
if self.placeholder_expression:
evaluator = PropertyMappingEvaluator(
@ -216,47 +211,6 @@ class Prompt(SerializerModel):
raise wrapped from exc
return self.placeholder
def get_initial_value(
self,
prompt_context: dict,
user: User,
request: HttpRequest,
dry_run: Optional[bool] = False,
) -> str:
"""Get fully interpolated initial value"""
if self.field_key in prompt_context:
# We don't want to parse this as an expression since a user will
# be able to control the input
value = prompt_context[self.field_key]
elif self.initial_value_expression:
evaluator = PropertyMappingEvaluator(
self, user, request, prompt_context=prompt_context, dry_run=dry_run
)
try:
value = evaluator.evaluate(self.initial_value)
except Exception as exc: # pylint:disable=broad-except
wrapped = PropertyMappingExpressionException(str(exc))
LOGGER.warning(
"failed to evaluate prompt initial value",
exc=wrapped,
)
if dry_run:
raise wrapped from exc
value = self.initial_value
else:
value = self.initial_value
if self.type in CHOICE_FIELDS:
# Ensure returned value is a valid choice
choices = self.get_choices(prompt_context, user, request)
if not choices:
return ""
if value not in choices:
return choices[0]
return value
def field(self, default: Optional[Any], choices: Optional[list[Any]] = None) -> CharField:
"""Get field type for Challenge and response. Choices are only valid for CHOICE_FIELDS."""
field_class = CharField

View File

@ -38,7 +38,6 @@ class StagePromptSerializer(PassiveSerializer):
type = ChoiceField(choices=FieldTypes.choices)
required = BooleanField()
placeholder = CharField(allow_blank=True)
initial_value = CharField(allow_blank=True)
order = IntegerField()
sub_text = CharField(allow_blank=True)
choices = ListField(child=CharField(allow_blank=True), allow_empty=True, allow_null=True)
@ -77,7 +76,7 @@ class PromptChallengeResponse(ChallengeResponse):
choices = field.get_choices(
plan.context.get(PLAN_CONTEXT_PROMPT, {}), user, self.request
)
current = field.get_initial_value(
current = field.get_placeholder(
plan.context.get(PLAN_CONTEXT_PROMPT, {}), user, self.request
)
self.fields[field.field_key] = field.field(current, choices)
@ -198,9 +197,8 @@ class PromptStageView(ChallengeStageView):
serializers = []
for field in fields:
data = StagePromptSerializer(field).data
# Ensure all choices, placeholders and initial values are str, as
# otherwise further in we can fail serializer validation if we return
# some types such as bool
# Ensure all choices and placeholders are str, as otherwise further in
# we can fail serializer validation if we return some types such as bool
choices = field.get_choices(context, self.get_pending_user(), self.request, dry_run)
if choices:
data["choices"] = [str(choice) for choice in choices]
@ -209,9 +207,6 @@ class PromptStageView(ChallengeStageView):
data["placeholder"] = str(
field.get_placeholder(context, self.get_pending_user(), self.request, dry_run)
)
data["initial_value"] = str(
field.get_initial_value(context, self.get_pending_user(), self.request, dry_run)
)
serializers.append(data)
return serializers

View File

@ -22,7 +22,6 @@ from authentik.stages.prompt.stage import (
)
# pylint: disable=too-many-public-methods
class TestPromptStage(FlowTestCase):
"""Prompt tests"""
@ -38,7 +37,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.USERNAME,
required=True,
placeholder="USERNAME_PLACEHOLDER",
initial_value="akuser",
)
text_prompt = Prompt.objects.create(
name=generate_id(),
@ -47,7 +45,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.TEXT,
required=True,
placeholder="TEXT_PLACEHOLDER",
initial_value="some text",
)
text_area_prompt = Prompt.objects.create(
name=generate_id(),
@ -56,7 +53,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.TEXT_AREA,
required=True,
placeholder="TEXT_AREA_PLACEHOLDER",
initial_value="some text",
)
email_prompt = Prompt.objects.create(
name=generate_id(),
@ -65,7 +61,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.EMAIL,
required=True,
placeholder="EMAIL_PLACEHOLDER",
initial_value="email@example.com",
)
password_prompt = Prompt.objects.create(
name=generate_id(),
@ -74,7 +69,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.PASSWORD,
required=True,
placeholder="PASSWORD_PLACEHOLDER",
initial_value="supersecurepassword",
)
password2_prompt = Prompt.objects.create(
name=generate_id(),
@ -83,7 +77,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.PASSWORD,
required=True,
placeholder="PASSWORD_PLACEHOLDER",
initial_value="supersecurepassword",
)
number_prompt = Prompt.objects.create(
name=generate_id(),
@ -92,7 +85,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.NUMBER,
required=True,
placeholder="NUMBER_PLACEHOLDER",
initial_value="42",
)
hidden_prompt = Prompt.objects.create(
name=generate_id(),
@ -100,7 +92,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.HIDDEN,
required=True,
placeholder="HIDDEN_PLACEHOLDER",
initial_value="something idk",
)
static_prompt = Prompt.objects.create(
name=generate_id(),
@ -108,7 +99,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.STATIC,
required=True,
placeholder="static",
initial_value="something idk",
)
radio_button_group = Prompt.objects.create(
name=generate_id(),
@ -116,7 +106,6 @@ class TestPromptStage(FlowTestCase):
type=FieldTypes.RADIO_BUTTON_GROUP,
required=True,
placeholder="test",
initial_value="test",
)
dropdown = Prompt.objects.create(
name=generate_id(),
@ -148,9 +137,9 @@ class TestPromptStage(FlowTestCase):
password_prompt.field_key: "test",
password2_prompt.field_key: "test",
number_prompt.field_key: 3,
hidden_prompt.field_key: hidden_prompt.initial_value,
static_prompt.field_key: static_prompt.initial_value,
radio_button_group.field_key: radio_button_group.initial_value,
hidden_prompt.field_key: hidden_prompt.placeholder,
static_prompt.field_key: static_prompt.placeholder,
radio_button_group.field_key: radio_button_group.placeholder,
dropdown.field_key: "",
}
@ -346,176 +335,106 @@ class TestPromptStage(FlowTestCase):
self.assertEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
def test_prompt_placeholder_does_not_take_value_from_context(self):
"""Test placeholder does not automatically take value from context"""
context = {
"foo": generate_id(),
}
prompt: Prompt = Prompt(
field_key="text_prompt_expression",
label="TEXT_LABEL",
type=FieldTypes.TEXT,
placeholder="return prompt_context['foo']",
placeholder_expression=True,
)
context["text_prompt_expression"] = generate_id()
self.assertEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
def test_prompt_initial_value(self):
"""Test initial_value and expression"""
context = {
"foo": generate_id(),
}
prompt: Prompt = Prompt(
field_key="text_prompt_expression",
label="TEXT_LABEL",
type=FieldTypes.TEXT,
initial_value="return prompt_context['foo']",
initial_value_expression=True,
)
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), context["foo"]
)
context["text_prompt_expression"] = generate_id()
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")),
prompt.get_placeholder(context, self.user, self.factory.get("/")),
context["text_prompt_expression"],
)
self.assertNotEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), context["foo"]
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
def test_choice_prompts_placeholder_and_initial_value_no_choices(self):
"""Test placeholder and initial value of choice fields with 0 choices"""
context = {}
def test_choice_prompts_placeholders(self):
"""Test placeholders and expression of choice fields"""
context = {"foo": generate_id()}
# No choices - unusable (in the sense it creates an unsubmittable form)
# but valid behaviour
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.RADIO_BUTTON_GROUP,
placeholder="return []",
placeholder_expression=True,
initial_value="Invalid choice",
initial_value_expression=False,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertEqual(prompt.get_initial_value(context, self.user, self.factory.get("/")), "")
self.assertEqual(prompt.get_choices(context, self.user, self.factory.get("/")), tuple())
def test_choice_prompts_placeholder_and_initial_value_single_choice(self):
"""Test placeholder and initial value of choice fields with 1 choice"""
context = {"foo": generate_id()}
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.DROPDOWN,
placeholder=context["foo"],
placeholder_expression=False,
initial_value=context["foo"],
initial_value_expression=False,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
context["fixed_choice_prompt_expression"] = generate_id()
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), (context["foo"],)
)
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.DROPDOWN,
placeholder="return [prompt_context['foo']]",
placeholder_expression=True,
initial_value="return prompt_context['foo']",
initial_value_expression=True,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), (context["foo"],)
)
def test_choice_prompts_placeholder_and_initial_value_multiple_choices(self):
"""Test placeholder and initial value of choice fields with multiple choices"""
context = {}
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.RADIO_BUTTON_GROUP,
placeholder="return ['test', True, 42]",
placeholder_expression=True,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), "test"
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), ("test", True, 42)
)
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.RADIO_BUTTON_GROUP,
placeholder="return ['test', True, 42]",
placeholder_expression=True,
initial_value="return True",
initial_value_expression=True,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertEqual(prompt.get_initial_value(context, self.user, self.factory.get("/")), True)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), ("test", True, 42)
)
def test_choice_prompts_placeholder_and_initial_value_from_context(self):
"""Test placeholder and initial value of choice fields with values from context"""
rand_value = generate_id()
context = {
"fixed_choice_prompt_expression": rand_value,
"fixed_choice_prompt_expression__choices": ["test", 42, rand_value],
}
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.RADIO_BUTTON_GROUP,
)
self.assertEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")), rand_value
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), ("test", 42, rand_value)
)
def test_initial_value_not_valid_choice(self):
"""Test initial_value not a valid choice"""
context = {}
prompt: Prompt = Prompt(
field_key="choice_prompt",
label="TEXT_LABEL",
type=FieldTypes.DROPDOWN,
placeholder="choice",
initial_value="another_choice",
prompt.get_placeholder(context, self.user, self.factory.get("/")),
context["fixed_choice_prompt_expression"],
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")),
("choice",),
(context["fixed_choice_prompt_expression"],),
)
self.assertNotEqual(prompt.get_placeholder(context, self.user, self.factory.get("/")), "")
self.assertNotEqual(prompt.get_choices(context, self.user, self.factory.get("/")), tuple())
del context["fixed_choice_prompt_expression"]
# Single choice
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.RADIO_BUTTON_GROUP,
placeholder="return prompt_context['foo']",
placeholder_expression=True,
)
self.assertEqual(
prompt.get_initial_value(context, self.user, self.factory.get("/")),
"choice",
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), (context["foo"],)
)
context["fixed_choice_prompt_expression"] = generate_id()
self.assertEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")),
context["fixed_choice_prompt_expression"],
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")),
(context["fixed_choice_prompt_expression"],),
)
self.assertNotEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertNotEqual(
prompt.get_choices(context, self.user, self.factory.get("/")), (context["foo"],)
)
del context["fixed_choice_prompt_expression"]
# Multi choice
prompt: Prompt = Prompt(
field_key="fixed_choice_prompt_expression",
label="LABEL",
type=FieldTypes.DROPDOWN,
placeholder="return [prompt_context['foo'], True, 'text']",
placeholder_expression=True,
)
self.assertEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")),
(context["foo"], True, "text"),
)
context["fixed_choice_prompt_expression"] = tuple(["text", generate_id(), 2])
self.assertEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")),
"text",
)
self.assertEqual(
prompt.get_choices(context, self.user, self.factory.get("/")),
context["fixed_choice_prompt_expression"],
)
self.assertNotEqual(
prompt.get_placeholder(context, self.user, self.factory.get("/")), context["foo"]
)
self.assertNotEqual(
prompt.get_choices(context, self.user, self.factory.get("/")),
(context["foo"], True, "text"),
)
def test_choices_are_none_for_non_choice_fields(self):
@ -586,8 +505,6 @@ class TestPromptStage(FlowTestCase):
"type": FieldTypes.TEXT,
"placeholder": 'return "Hello world"',
"placeholder_expression": True,
"initial_value": 'return "Hello Hello world"',
"initial_value_expression": True,
"sub_text": "test",
"order": 123,
},
@ -605,7 +522,6 @@ class TestPromptStage(FlowTestCase):
"type": "text",
"required": True,
"placeholder": "Hello world",
"initial_value": "Hello Hello world",
"order": 123,
"sub_text": "test",
"choices": None,

View File

@ -3,7 +3,9 @@
import uuid
import django.db.models.deletion
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.lib.utils.time

View File

@ -13,14 +13,12 @@ entries:
id: flow
- attrs:
order: 200
placeholder: Username
placeholder_expression: false
initial_value: |
placeholder: |
try:
return user.username
except:
return ''
initial_value_expression: true
placeholder_expression: true
required: true
type: text
field_key: username
@ -31,14 +29,12 @@ entries:
model: authentik_stages_prompt.prompt
- attrs:
order: 201
placeholder: Name
placeholder_expression: false
initial_value: |
placeholder: |
try:
return user.name
except:
return ''
initial_value_expression: true
placeholder_expression: true
required: true
type: text
field_key: name
@ -49,14 +45,12 @@ entries:
model: authentik_stages_prompt.prompt
- attrs:
order: 202
placeholder: Email
placeholder_expression: false
initial_value: |
placeholder: |
try:
return user.email
except:
return ''
initial_value_expression: true
placeholder_expression: true
required: true
type: email
field_key: email
@ -67,14 +61,12 @@ entries:
model: authentik_stages_prompt.prompt
- attrs:
order: 203
placeholder: Locale
placeholder_expression: false
initial_value: |
placeholder: |
try:
return user.attributes.get("settings", {}).get("locale", "")
except:
return ''
initial_value_expression: true
placeholder_expression: true
required: true
type: ak-locale
field_key: attributes.settings.locale

View File

@ -6,13 +6,11 @@ import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/common"
"goauthentik.io/internal/config"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/ak/healthcheck"
"goauthentik.io/internal/outpost/ldap"
)
@ -23,72 +21,59 @@ Required environment variables:
- AUTHENTIK_TOKEN: Token to authenticate with
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
var rootCmd = &cobra.Command{
Long: helpMessage,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
},
Run: func(cmd *cobra.Command, args []string) {
debug.EnableDebugServer()
akURL := config.Get().AuthentikHost
if akURL == "" {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken := config.Get().AuthentikToken
if akToken == "" {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = ldap.NewServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
},
}
func main() {
rootCmd.AddCommand(healthcheck.Command)
err := rootCmd.Execute()
if err != nil {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
debug.EnableDebugServer()
akURL := config.Get().AuthentikHost
if akURL == "" {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken := config.Get().AuthentikToken
if akToken == "" {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = ldap.NewServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
}

View File

@ -6,13 +6,11 @@ import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/common"
"goauthentik.io/internal/config"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/ak/healthcheck"
"goauthentik.io/internal/outpost/proxyv2"
)
@ -26,72 +24,59 @@ Required environment variables:
Optionally, you can set these:
- AUTHENTIK_HOST_BROWSER: URL to use in the browser, when it differs from AUTHENTIK_HOST`
var rootCmd = &cobra.Command{
Long: helpMessage,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
},
Run: func(cmd *cobra.Command, args []string) {
debug.EnableDebugServer()
akURL := config.Get().AuthentikHost
if akURL == "" {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken := config.Get().AuthentikToken
if akToken == "" {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = proxyv2.NewProxyServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
},
}
func main() {
rootCmd.AddCommand(healthcheck.Command)
err := rootCmd.Execute()
if err != nil {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
debug.EnableDebugServer()
akURL := config.Get().AuthentikHost
if akURL == "" {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken := config.Get().AuthentikToken
if akToken == "" {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = proxyv2.NewProxyServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
}

View File

@ -1,94 +0,0 @@
package main
import (
"fmt"
"net/url"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/common"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/ak/healthcheck"
"goauthentik.io/internal/outpost/radius"
)
const helpMessage = `authentik radius
Required environment variables:
- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company")
- AUTHENTIK_TOKEN: Token to authenticate with
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
var rootCmd = &cobra.Command{
Long: helpMessage,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
},
Run: func(cmd *cobra.Command, args []string) {
debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
if !found {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken, found := os.LookupEnv("AUTHENTIK_TOKEN")
if !found {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = radius.NewServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
},
}
func main() {
rootCmd.AddCommand(healthcheck.Command)
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}

78
cmd/radius/server.go Normal file
View File

@ -0,0 +1,78 @@
package main
import (
"fmt"
"net/url"
"os"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/common"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/radius"
)
const helpMessage = `authentik radius
Required environment variables:
- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company")
- AUTHENTIK_TOKEN: Token to authenticate with
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
func main() {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
go debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")
if !found {
fmt.Println("env AUTHENTIK_HOST not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akToken, found := os.LookupEnv("AUTHENTIK_TOKEN")
if !found {
fmt.Println("env AUTHENTIK_TOKEN not set!")
fmt.Println(helpMessage)
os.Exit(1)
}
akURLActual, err := url.Parse(akURL)
if err != nil {
fmt.Println(err)
fmt.Println(helpMessage)
os.Exit(1)
}
ex := common.Init()
defer common.Defer()
go func() {
for {
<-ex
os.Exit(0)
}
}()
ac := ak.NewAPIController(*akURLActual, akToken)
if ac == nil {
os.Exit(1)
}
defer ac.Shutdown()
ac.Server = radius.NewServer(ac)
err = ac.Start()
if err != nil {
log.WithError(err).Panic("Failed to run server")
}
for {
<-ex
}
}

View File

@ -1,76 +0,0 @@
package main
import (
"fmt"
"net/http"
"os"
"path"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/config"
"goauthentik.io/internal/utils/web"
)
var workerHeartbeat = path.Join(os.TempDir(), "authentik-worker")
const workerThreshold = 30
var healthcheckCmd = &cobra.Command{
Use: "healthcheck",
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
os.Exit(1)
}
mode := args[0]
exitCode := 1
log.WithField("mode", mode).Debug("checking health")
switch strings.ToLower(mode) {
case "server":
exitCode = checkServer()
case "worker":
exitCode = checkWorker()
default:
log.Warn("Invalid mode")
}
os.Exit(exitCode)
},
}
func init() {
rootCmd.AddCommand(healthcheckCmd)
}
func checkServer() int {
h := &http.Client{
Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport),
}
url := fmt.Sprintf("http://%s/-/health/ready/", config.Get().Listen.HTTP)
res, err := h.Head(url)
if err != nil {
log.WithError(err).Warning("failed to send healthcheck request")
return 1
}
if res.StatusCode >= 400 {
log.WithField("status", res.StatusCode).Warning("unhealthy status code")
return 1
}
log.Debug("successfully checked health")
return 0
}
func checkWorker() int {
stat, err := os.Stat(workerHeartbeat)
if err != nil {
log.WithError(err).Warning("failed to check worker heartbeat file")
return 1
}
delta := time.Since(stat.ModTime()).Seconds()
if delta > workerThreshold {
log.WithField("threshold", workerThreshold).WithField("delta", delta).Warning("Worker hasn't updated heartbeat in threshold")
return 1
}
return 0
}

View File

@ -1,10 +1,132 @@
package main
import "os"
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/getsentry/sentry-go"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/common"
"goauthentik.io/internal/config"
"goauthentik.io/internal/constants"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/gounicorn"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2"
sentryutils "goauthentik.io/internal/utils/sentry"
webutils "goauthentik.io/internal/utils/web"
"goauthentik.io/internal/web"
"goauthentik.io/internal/web/tenant_tls"
)
var running = true
func main() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
debug.EnableDebugServer()
l := log.WithField("logger", "authentik.root")
if config.Get().ErrorReporting.Enabled {
err := sentry.Init(sentry.ClientOptions{
Dsn: config.Get().ErrorReporting.SentryDSN,
AttachStacktrace: true,
EnableTracing: true,
TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate),
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
Environment: config.Get().ErrorReporting.Environment,
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgent(), http.DefaultTransport),
IgnoreErrors: []string{
http.ErrAbortHandler.Error(),
},
})
if err != nil {
l.WithError(err).Warning("failed to init sentry")
}
}
ex := common.Init()
defer common.Defer()
u, _ := url.Parse("http://localhost:8000")
g := gounicorn.New()
defer func() {
l.Info("shutting down gunicorn")
g.Kill()
}()
ws := web.NewWebServer(g)
g.HealthyCallback = func() {
if !config.Get().Outposts.DisableEmbeddedOutpost {
go attemptProxyStart(ws, u)
}
}
go web.RunMetricsServer()
go attemptStartBackend(g)
ws.Start()
<-ex
running = false
l.Info("shutting down webserver")
go ws.Shutdown()
}
func attemptStartBackend(g *gounicorn.GoUnicorn) {
for {
if !running {
return
}
err := g.Start()
log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting")
}
}
func attemptProxyStart(ws *web.WebServer, u *url.URL) {
maxTries := 100
attempt := 0
l := log.WithField("logger", "authentik.server")
for {
l.Debug("attempting to init outpost")
ac := ak.NewAPIController(*u, config.Get().SecretKey)
if ac == nil {
attempt += 1
time.Sleep(1 * time.Second)
if attempt > maxTries {
break
}
continue
}
// Init tenant_tls here too since it requires an API Client,
// so we just re-use the same one as the outpost uses
tw := tenant_tls.NewWatcher(ac.Client)
go tw.Start()
ws.TenantTLS = tw
ac.AddRefreshHandler(func() {
tw.Check()
})
srv := proxyv2.NewProxyServer(ac)
ws.ProxyServer = srv
ac.Server = srv
l.Debug("attempting to start outpost")
err := ac.StartBackgroundTasks()
if err != nil {
l.WithError(err).Warning("outpost failed to start")
attempt += 1
time.Sleep(15 * time.Second)
if attempt > maxTries {
break
}
continue
} else {
select {}
}
}
}

View File

@ -1,141 +0,0 @@
package main
import (
"fmt"
"net/http"
"net/url"
"time"
"github.com/getsentry/sentry-go"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/common"
"goauthentik.io/internal/config"
"goauthentik.io/internal/constants"
"goauthentik.io/internal/debug"
"goauthentik.io/internal/gounicorn"
"goauthentik.io/internal/outpost/ak"
"goauthentik.io/internal/outpost/proxyv2"
sentryutils "goauthentik.io/internal/utils/sentry"
webutils "goauthentik.io/internal/utils/web"
"goauthentik.io/internal/web"
"goauthentik.io/internal/web/tenant_tls"
)
var running = true
var rootCmd = &cobra.Command{
Use: "authentik",
Short: "Start authentik instance",
Version: constants.FullVersion(),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.DebugLevel)
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
},
Run: func(cmd *cobra.Command, args []string) {
debug.EnableDebugServer()
l := log.WithField("logger", "authentik.root")
if config.Get().ErrorReporting.Enabled {
err := sentry.Init(sentry.ClientOptions{
Dsn: config.Get().ErrorReporting.SentryDSN,
AttachStacktrace: true,
EnableTracing: true,
TracesSampler: sentryutils.SamplerFunc(config.Get().ErrorReporting.SampleRate),
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
Environment: config.Get().ErrorReporting.Environment,
HTTPTransport: webutils.NewUserAgentTransport(constants.UserAgent(), http.DefaultTransport),
IgnoreErrors: []string{
http.ErrAbortHandler.Error(),
},
})
if err != nil {
l.WithError(err).Warning("failed to init sentry")
}
}
ex := common.Init()
defer common.Defer()
u, _ := url.Parse("http://localhost:8000")
g := gounicorn.New()
defer func() {
l.Info("shutting down gunicorn")
g.Kill()
}()
ws := web.NewWebServer(g)
g.HealthyCallback = func() {
if !config.Get().Outposts.DisableEmbeddedOutpost {
go attemptProxyStart(ws, u)
}
}
go web.RunMetricsServer()
go attemptStartBackend(g)
ws.Start()
<-ex
running = false
l.Info("shutting down webserver")
go ws.Shutdown()
},
}
func attemptStartBackend(g *gounicorn.GoUnicorn) {
for {
if !running {
return
}
err := g.Start()
log.WithField("logger", "authentik.router").WithError(err).Warning("gunicorn process died, restarting")
}
}
func attemptProxyStart(ws *web.WebServer, u *url.URL) {
maxTries := 100
attempt := 0
l := log.WithField("logger", "authentik.server")
for {
l.Debug("attempting to init outpost")
ac := ak.NewAPIController(*u, config.Get().SecretKey)
if ac == nil {
attempt += 1
time.Sleep(1 * time.Second)
if attempt > maxTries {
break
}
continue
}
// Init tenant_tls here too since it requires an API Client,
// so we just re-use the same one as the outpost uses
tw := tenant_tls.NewWatcher(ac.Client)
go tw.Start()
ws.TenantTLS = tw
ac.AddRefreshHandler(func() {
tw.Check()
})
srv := proxyv2.NewProxyServer(ac)
ws.ProxyServer = srv
ac.Server = srv
l.Debug("attempting to start outpost")
err := ac.StartBackgroundTasks()
if err != nil {
l.WithError(err).Warning("outpost failed to start")
attempt += 1
time.Sleep(15 * time.Second)
if attempt > maxTries {
break
}
continue
} else {
select {}
}
}
}

View File

@ -1,5 +1,5 @@
---
version: "3.4"
version: '3.4'
services:
postgresql:
@ -14,9 +14,9 @@ services:
volumes:
- database:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${PG_PASS:?database password required}
POSTGRES_USER: ${PG_USER:-authentik}
POSTGRES_DB: ${PG_DB:-authentik}
- POSTGRES_PASSWORD=${PG_PASS:?database password required}
- POSTGRES_USER=${PG_USER:-authentik}
- POSTGRES_DB=${PG_DB:-authentik}
env_file:
- .env
redis:
@ -32,7 +32,7 @@ services:
volumes:
- redis:/data
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.4.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.4.0}
restart: unless-stopped
command: server
environment:
@ -47,10 +47,10 @@ services:
env_file:
- .env
ports:
- "${COMPOSE_PORT_HTTP:-9000}:9000"
- "${COMPOSE_PORT_HTTPS:-9443}:9443"
- "${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.4.1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.4.0}
restart: unless-stopped
command: worker
environment:

25
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/getsentry/sentry-go v0.20.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
github.com/go-ldap/ldap/v3 v3.4.4
github.com/go-openapi/runtime v0.26.0
github.com/go-openapi/runtime v0.25.0
github.com/go-openapi/strfmt v0.21.7
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.3.0
@ -24,9 +24,8 @@ require (
github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.15.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2
goauthentik.io/api/v3 v3.2023041.3
goauthentik.io/api/v3 v3.2023031.17
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
golang.org/x/oauth2 v0.7.0
golang.org/x/sync v0.1.0
@ -37,6 +36,8 @@ require (
require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
@ -47,16 +48,15 @@ require (
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/spec v0.20.8 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/validate v0.22.1 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.21.1 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-openapi/validate v0.21.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
@ -69,10 +69,9 @@ require (
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect

57
go.sum
View File

@ -38,7 +38,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb h1:w9IDEB7P1VzNcBpOG7kMpFkZp2DkyJIUt0gDx5MBhRU=
github.com/Netflix/go-env v0.0.0-20210215222557-e437a7e7f9fb/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@ -55,7 +57,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -89,41 +90,33 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc=
github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0=
github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc=
github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ=
github.com/go-openapi/runtime v0.25.0 h1:7yQTCdRbWhX8vnIjdzU8S00tBYf7Sg71EBeorlPHvhc=
github.com/go-openapi/runtime v0.25.0/go.mod h1:Ux6fikcHXyyob6LNWxtE96hWwjBPYF0DXgVFuMTneOs=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU=
github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k=
github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU=
github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI=
github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
@ -222,8 +215,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU=
github.com/jellydator/ttlcache/v3 v3.0.1/go.mod h1:WwTaEmcXQ3MTjOm4bsZoDFiCu/hMvNWLO1w67RXz6h4=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
@ -292,18 +283,13 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@ -332,7 +318,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -340,14 +325,14 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZpKxs=
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
goauthentik.io/api/v3 v3.2023041.3 h1:2NkeBK1LywjcWYGLZjw9gmscTyOfAeRjUWfBZfq9aeU=
goauthentik.io/api/v3 v3.2023041.3/go.mod h1:A2I2iDSEu0pW13mAT6J6wMWVSeV5+F52MWlcIHmERvk=
goauthentik.io/api/v3 v3.2023031.17 h1:FM1kG/1TBy8aCsWZtiuapp7J451Z3xWpj0dTSfjtnaE=
goauthentik.io/api/v3 v3.2023031.17/go.mod h1:H76Cdv9Nio0vnivoh6u12nlVIrW6x/kkvUwj8udFs4s=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
@ -427,6 +412,7 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -434,6 +420,7 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -487,12 +474,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -502,6 +490,7 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -30,7 +30,7 @@ type RedisConfig struct {
Password string `yaml:"password" env:"AUTHENTIK_REDIS__PASSWORD"`
TLS bool `yaml:"tls" env:"AUTHENTIK_REDIS__TLS"`
TLSReqs string `yaml:"tls_reqs" env:"AUTHENTIK_REDIS__TLS_REQS"`
DB int `yaml:"cache_db" env:"AUTHENTIK_REDIS__DB"`
DB int `yaml:"cache_db" env:"AUTHENTIK_REDIS__CACHE_DB"`
CacheTimeout int `yaml:"cache_timeout" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT"`
CacheTimeoutFlows int `yaml:"cache_timeout_flows" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT_FLOWS"`
CacheTimeoutPolicies int `yaml:"cache_timeout_policies" env:"AUTHENTIK_REDIS__CACHE_TIMEOUT_POLICIES"`

View File

@ -29,4 +29,4 @@ func UserAgent() string {
return fmt.Sprintf("authentik@%s", FullVersion())
}
const VERSION = "2023.4.1"
const VERSION = "2023.4.0"

View File

@ -1,38 +0,0 @@
package healthcheck
import (
"fmt"
"net/http"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"goauthentik.io/internal/config"
"goauthentik.io/internal/utils/web"
)
var Command = &cobra.Command{
Use: "healthcheck",
Run: func(cmd *cobra.Command, args []string) {
config.Get()
os.Exit(check())
},
}
func check() int {
h := &http.Client{
Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport),
}
url := fmt.Sprintf("http://%s/outpost.goauthentik.io/ping", config.Get().Listen.Metrics)
res, err := h.Head(url)
if err != nil {
log.WithError(err).Warning("failed to send healthcheck request")
return 1
}
if res.StatusCode >= 400 {
log.WithField("status", res.StatusCode).Warning("unhealthy status code")
return 1
}
log.Debug("successfully checked health")
return 0
}

View File

@ -13,16 +13,33 @@ import (
func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
dn := pi.GetUserDN(u.Username)
attrs := utils.AttributesToLDAP(u.Attributes, func(key string) string {
return utils.AttributeKeySanitize(key)
}, func(value []string) []string {
userValueMap := func(value []string) []string {
for i, v := range value {
if strings.Contains(v, "%s") {
value[i] = fmt.Sprintf(v, u.Username)
}
}
return value
})
}
attrs := utils.AttributesToLDAP(u.Attributes, func(key string) string {
return utils.AttributeKeySanitize(key)
}, userValueMap)
rawAttrs := utils.AttributesToLDAP(u.Attributes, func(key string) string {
return key
}, userValueMap)
// Only append attributes that don't already exist
// TODO: Remove in 2023.3
for _, rawAttr := range rawAttrs {
exists := false
for _, attr := range attrs {
if strings.EqualFold(attr.Name, rawAttr.Name) {
exists = true
}
}
if !exists {
attrs = append(attrs, rawAttr)
}
}
if u.IsActive == nil {
u.IsActive = api.PtrBool(false)
@ -31,6 +48,10 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
u.Email = api.PtrString("")
}
attrs = utils.EnsureAttributes(attrs, map[string][]string{
// Old fields for backwards compatibility
"goauthentik.io/ldap/active": {strconv.FormatBool(*u.IsActive)},
"goauthentik.io/ldap/superuser": {strconv.FormatBool(u.IsSuperuser)},
// End old fields
"ak-active": {strconv.FormatBool(*u.IsActive)},
"ak-superuser": {strconv.FormatBool(u.IsSuperuser)},
"memberOf": pi.GroupsForUser(u),

View File

@ -2,6 +2,7 @@ package group
import (
"strconv"
"strings"
"github.com/nmcclain/ldap"
"goauthentik.io/api/v3"
@ -27,6 +28,24 @@ func (lg *LDAPGroup) Entry() *ldap.Entry {
}, func(value []string) []string {
return value
})
rawAttrs := utils.AttributesToLDAP(lg.Attributes, func(key string) string {
return key
}, func(value []string) []string {
return value
})
// Only append attributes that don't already exist
// TODO: Remove in 2023.3
for _, rawAttr := range rawAttrs {
exists := false
for _, attr := range attrs {
if strings.EqualFold(attr.Name, rawAttr.Name) {
exists = true
}
}
if !exists {
attrs = append(attrs, rawAttr)
}
}
objectClass := []string{constants.OCGroup, constants.OCGroupOfUniqueNames, constants.OCGroupOfNames, constants.OCAKGroup, constants.OCPosixGroup}
if lg.IsVirtualGroup {
@ -34,6 +53,9 @@ func (lg *LDAPGroup) Entry() *ldap.Entry {
}
attrs = utils.EnsureAttributes(attrs, map[string][]string{
// Old fields for backwards compatibility
"goauthentik.io/ldap/superuser": {strconv.FormatBool(lg.IsSuperuser)},
// End old fields
"ak-superuser": {strconv.FormatBool(lg.IsSuperuser)},
"objectClass": objectClass,
"member": lg.Member,

View File

@ -19,7 +19,7 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
COPY --from=builder /go/ldap /
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/ldap", "healthcheck" ]
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ]
EXPOSE 3389 6636 9300

View File

@ -1,5 +1,6 @@
#!/bin/bash -e
MODE_FILE="${TMPDIR}/authentik-mode"
WORKER_HEARTBEAT="${TMPDIR}/authentik-worker"
function log {
printf '{"event": "%s", "level": "info", "logger": "bootstrap"}\n' "$@" > /dev/stderr
@ -14,7 +15,7 @@ function wait_for_db {
function check_if_root {
if [[ $EUID -ne 0 ]]; then
log "Not running as root, disabling permission fixes"
exec $1
$1
return
fi
SOCKET="/var/run/docker.sock"
@ -34,15 +35,7 @@ function check_if_root {
chown -R authentik:authentik /media /certs
chmod ug+rwx /media
chmod ug+rx /certs
exec chpst -u authentik:$GROUP env HOME=/authentik $1
}
function run_authentik {
if [[ -x "$(command -v authentik)" ]]; then
exec authentik $@
else
exec go run -v ./cmd/server/ $@
fi
chpst -u authentik:$GROUP env HOME=/authentik $1
}
function set_mode {
@ -63,7 +56,11 @@ if [[ "$1" == "server" ]]; then
if [[ ! -z "${AUTHENTIK_BOOTSTRAP_PASSWORD}" || ! -z "${AUTHENTIK_BOOTSTRAP_TOKEN}" ]]; then
python -m manage bootstrap_tasks
fi
run_authentik
if [[ -x "$(command -v authentik)" ]]; then
authentik
else
go run -v ./cmd/server/
fi
elif [[ "$1" == "worker" ]]; then
wait_for_db
set_mode "worker"
@ -80,7 +77,18 @@ elif [[ "$1" == "test-all" ]]; then
chown authentik:authentik /unittest.xml
check_if_root "python -m manage test authentik"
elif [[ "$1" == "healthcheck" ]]; then
run_authentik healthcheck $(cat $MODE_FILE)
mode=$(cat $MODE_FILE)
if [[ $mode == "server" ]]; then
exec curl --user-agent "goauthentik.io lifecycle Healthcheck" -I http://localhost:9000/-/health/ready/
elif [[ $mode == "worker" ]]; then
mtime=$(stat -f %m $WORKER_HEARTBEAT)
time=$(date +"%s")
if [ "$(( $time - $mtime ))" -gt "30" ]; then
log "Worker hasn't updated heartbeat in 30 seconds"
exit 1
fi
exit 0
fi
elif [[ "$1" == "dump_config" ]]; then
exec python -m authentik.lib.config
elif [[ "$1" == "debug" ]]; then

View File

@ -155,6 +155,6 @@ if not CONFIG.y_bool("disable_startup_analytics", False):
},
timeout=5,
)
# pylint: disable=broad-exception-caught
except Exception: # nosec
# pylint: disable=bare-except
except: # nosec
pass

164
poetry.lock generated
View File

@ -175,64 +175,6 @@ doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16,<0.22)"]
[[package]]
name = "argon2-cffi"
version = "21.3.0"
description = "The secure Argon2 password hashing algorithm."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"},
{file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"},
]
[package.dependencies]
argon2-cffi-bindings = "*"
[package.extras]
dev = ["cogapp", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "pre-commit", "pytest", "sphinx", "sphinx-notfound-page", "tomli"]
docs = ["furo", "sphinx", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
[[package]]
name = "argon2-cffi-bindings"
version = "21.2.0"
description = "Low-level CFFI bindings for Argon2"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"},
{file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"},
{file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"},
{file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"},
{file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"},
{file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"},
{file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"},
{file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"},
{file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"},
{file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"},
{file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"},
{file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"},
{file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"},
]
[package.dependencies]
cffi = ">=1.0.1"
[package.extras]
dev = ["cogapp", "pre-commit", "pytest", "wheel"]
tests = ["pytest"]
[[package]]
name = "asgiref"
version = "3.5.2"
@ -300,6 +242,20 @@ files = [
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
]
[[package]]
name = "asyncio"
version = "3.4.3"
description = "reference implementation of PEP 3156"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"},
{file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"},
{file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"},
{file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"},
]
[[package]]
name = "attrs"
version = "22.1.0"
@ -1274,14 +1230,14 @@ ssh = ["paramiko (>=2.4.3)"]
[[package]]
name = "drf-spectacular"
version = "0.26.2"
version = "0.26.1"
description = "Sane and flexible OpenAPI 3 schema generation for Django REST framework"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "drf-spectacular-0.26.2.tar.gz", hash = "sha256:005623d6bb9de37d2d0ec24ccd59c636e4a42f9af252f1470129ac32ccab38cb"},
{file = "drf_spectacular-0.26.2-py3-none-any.whl", hash = "sha256:e80eba58d9579bf6c3380ffd6d6a9b466c4bc35b23da0ba76dfcc96de1e907d7"},
{file = "drf-spectacular-0.26.1.tar.gz", hash = "sha256:1599a204bf9cc6be7ef3e509859885a38d4f871fe287a1f191479868afd9e234"},
{file = "drf_spectacular-0.26.1-py3-none-any.whl", hash = "sha256:6df86ff6c2dc663792e5ff618643bf41d2ac9dc6fb5d1b0f273e2778bab951e5"},
]
[package.dependencies]
@ -1676,14 +1632,14 @@ files = [
[[package]]
name = "importlib-metadata"
version = "6.6.0"
version = "6.3.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "importlib_metadata-6.6.0-py3-none-any.whl", hash = "sha256:43dd286a2cd8995d5eaef7fee2066340423b818ed3fd70adf0bad5f1fac53fed"},
{file = "importlib_metadata-6.6.0.tar.gz", hash = "sha256:92501cdf9cc66ebd3e612f1b4f0c0765dfa42f0fa38ffb319b6bd84dd675d705"},
{file = "importlib_metadata-6.3.0-py3-none-any.whl", hash = "sha256:8f8bd2af397cf33bd344d35cfe7f489219b7d14fc79a3f854b75b8417e9226b0"},
{file = "importlib_metadata-6.3.0.tar.gz", hash = "sha256:23c2bcae4762dfb0bbe072d358faec24957901d75b6c4ab11172c0c982532402"},
]
[package.dependencies]
@ -2786,14 +2742,14 @@ files = [
[[package]]
name = "pytest"
version = "7.3.1"
version = "7.3.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"},
{file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"},
{file = "pytest-7.3.0-py3-none-any.whl", hash = "sha256:933051fa1bfbd38a21e73c3960cebdad4cf59483ddba7696c48509727e17f201"},
{file = "pytest-7.3.0.tar.gz", hash = "sha256:58ecc27ebf0ea643ebfdf7fb1249335da761a00c9f955bcd922349bcb68ee57d"},
]
[package.dependencies]
@ -2824,21 +2780,6 @@ pytest = ">=5.4.0"
docs = ["sphinx", "sphinx-rtd-theme"]
testing = ["Django", "django-configurations (>=2.0)"]
[[package]]
name = "pytest-github-actions-annotate-failures"
version = "0.1.8"
description = "pytest plugin to annotate failed tests with a workflow command for GitHub Actions"
category = "dev"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
files = [
{file = "pytest-github-actions-annotate-failures-0.1.8.tar.gz", hash = "sha256:2d6e6cb5f8d0aae4a27a20cc4e20fabd3199a121c57f44bc48fe28e372e0be23"},
{file = "pytest_github_actions_annotate_failures-0.1.8-py2.py3-none-any.whl", hash = "sha256:6a882ff21672fa79deae8d917eb965a6bde2b25191e7632e1adfc23ffac008ab"},
]
[package.dependencies]
pytest = ">=4.0.0"
[[package]]
name = "pytest-randomly"
version = "3.12.0"
@ -3084,43 +3025,16 @@ files = [
[package.dependencies]
pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
version = "0.0.262"
description = "An extremely fast Python linter, written in Rust."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.0.262-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c26c1abd420d041592d05d63aee8c6a18feb24aed4deb6e91129e9f2c7b4914a"},
{file = "ruff-0.0.262-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b379e9765afa679316e52288a942df085e590862f8945088936a7bce3116d8f3"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7e0ca6821aafbd2b059df3119fcd5881250721ca8e825789fd2c471f7c59be"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cca35e2aeddff72bb4379a1dabc134e0c0d25ebc754a2cb733a1f8d4dbbb5e0"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15bf5533ce169aebbafa00017987f673e879f60a625d932b464b8cdaf32a4fce"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3909e249d984c4517194005a1c30eaa0c3a6d906c789d9fc0c9c7e007fb3e759"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e2813013a19b3e147e840bdb2e42db5825b53b47364e58e7b467c5fa47ffda2"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d25a94996b2037e566c2a801c8b324c0a826194d5d4d90ad7c1ccb8cf06521fa"},
{file = "ruff-0.0.262-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ca04348372efc59f6ee808d903d35e0d352cf2c78e487757cd48b65104b83e"},
{file = "ruff-0.0.262-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:24f989363e9bb5d0283490298102a5218682e49ebf300e445d69e24bee03ac83"},
{file = "ruff-0.0.262-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3c24e678e43ca4b67e29cc9a7a54eea05f31a5898cbf17bfec47b68f08d32a60"},
{file = "ruff-0.0.262-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0baff3c9a22227358ea109c165efe62dbdd0f2b9fd5256567dda8682b444fe23"},
{file = "ruff-0.0.262-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:083bac6e238d8b7d5ac3618666ea63b7ac661cf94c5da160070a58e190082831"},
{file = "ruff-0.0.262-py3-none-win32.whl", hash = "sha256:15bbfa2d15c137717627e0d56b0e535ae297b734551e34e03fcc25d7642cf43a"},
{file = "ruff-0.0.262-py3-none-win_amd64.whl", hash = "sha256:973ac29193f718349cf5746b7d86dfeaf7d40e9651ed97790a9b9327305888b9"},
{file = "ruff-0.0.262-py3-none-win_arm64.whl", hash = "sha256:f102904ebe395acd2a181d295b98120acd7a63f732b691672977fc688674f4af"},
{file = "ruff-0.0.262.tar.gz", hash = "sha256:faea54231c265f5349975ba6f3d855b71881a01f391b2000c47740390c6d5f68"},
]
[[package]]
name = "selenium"
version = "4.9.0"
version = "4.8.3"
description = ""
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "selenium-4.9.0-py3-none-any.whl", hash = "sha256:4c19e6aac202719373108d53a5a8e9336ba8d2b25822ca32ae6ff37acbabbdbe"},
{file = "selenium-4.9.0.tar.gz", hash = "sha256:478fae77cdfaec32adb1e68d59632c8c191f920535282abcaa2d1a3d98655624"},
{file = "selenium-4.8.3-py3-none-any.whl", hash = "sha256:28430ac54a54fa59ad1f5392a1b89b169fe3ab2c2ccd1a9a10b6fe74f36cd6da"},
{file = "selenium-4.8.3.tar.gz", hash = "sha256:61cda3a304f82637162bc155cae7bf88fdb04c115fa2cb1c1c2e1358fcd19a9f"},
]
[package.dependencies]
@ -3131,14 +3045,14 @@ urllib3 = {version = ">=1.26,<2.0", extras = ["socks"]}
[[package]]
name = "sentry-sdk"
version = "1.20.0"
version = "1.19.1"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "sentry-sdk-1.20.0.tar.gz", hash = "sha256:a3410381ae769a436c0852cce140a5e5e49f566a07fb7c2ab445af1302f6ad89"},
{file = "sentry_sdk-1.20.0-py2.py3-none-any.whl", hash = "sha256:0ad6bbbe78057b8031a07de7aca6d2a83234e51adc4d436eaf8d8c697184db71"},
{file = "sentry-sdk-1.19.1.tar.gz", hash = "sha256:7ae78bd921981a5010ab540d6bdf3b793659a4db8cccf7f16180702d48a80d84"},
{file = "sentry_sdk-1.19.1-py2.py3-none-any.whl", hash = "sha256:885a11c69df23e53eb281d003b9ff15a5bdfa43d8a2a53589be52104a1b4582f"},
]
[package.dependencies]
@ -3263,21 +3177,16 @@ files = [
[[package]]
name = "sqlparse"
version = "0.4.4"
version = "0.4.3"
description = "A non-validating SQL parser."
category = "main"
optional = false
python-versions = ">=3.5"
files = [
{file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
{file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
{file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"},
{file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"},
]
[package.extras]
dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "stevedore"
version = "4.1.1"
@ -3413,19 +3322,20 @@ wsproto = ">=0.14"
[[package]]
name = "twilio"
version = "8.1.0"
version = "8.0.0"
description = "Twilio API client and TwiML generator"
category = "main"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "twilio-8.1.0-py2.py3-none-any.whl", hash = "sha256:19be48f21e799b9dd10e2e0a5633962438e04842864e806409f4f2dbe446a868"},
{file = "twilio-8.1.0.tar.gz", hash = "sha256:a31863119655cd3643f788099f6ea3fe74eea59ce3f65600f9a4931301311c08"},
{file = "twilio-8.0.0-py2.py3-none-any.whl", hash = "sha256:40212f0e03947201e0a8f84e6c2a08acd6e61f31f0c94ec04e6b9abae109113a"},
{file = "twilio-8.0.0.tar.gz", hash = "sha256:ee2eaaf24df4361200f18d04e14b9937de2c31f3322dd739491590d8ff7196fc"},
]
[package.dependencies]
aiohttp = ">=3.8.4"
aiohttp-retry = ">=2.8.3"
asyncio = ">=3.4.3"
PyJWT = ">=2.0.0,<3.0.0"
pytz = "*"
requests = ">=2.0.0"
@ -4152,4 +4062,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "82fc267d6041997d1410a951033cdb9f6c57d91df7d48acaecdbab320daab58e"
content-hash = "61d20a7f2b76b6554b4c2095db152395c41d6c650b2f7f16662d5ed2f8cc0983"

View File

@ -1,5 +1,5 @@
# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/node:20 as web-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:18 as web-builder
COPY ./web /static/
@ -32,7 +32,7 @@ COPY --from=web-builder /static/security.txt /web/security.txt
COPY --from=web-builder /static/dist/ /web/dist/
COPY --from=web-builder /static/authentik/ /web/authentik/
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/proxy", "healthcheck" ]
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ]
EXPOSE 9000 9300 9443

View File

@ -17,11 +17,6 @@ line-length = 100
target-version = ['py311']
exclude = 'node_modules'
[tool.ruff]
line-length = 100
target-version = "py311"
exclude = ["**/migrations/**", "**/node_modules/**"]
[tool.isort]
multi_line_output = 3
include_trailing_comma = true
@ -35,12 +30,12 @@ force_to_top = "*"
source = ["authentik"]
relative_files = true
omit = [
"*/asgi.py",
"manage.py",
"*/migrations/*",
"*/management/commands/*",
"*/apps.py",
"website/",
"*/asgi.py",
"manage.py",
"*/migrations/*",
"*/management/commands/*",
"*/apps.py",
"website/",
]
[tool.coverage.report]
@ -48,19 +43,19 @@ sort = "Cover"
skip_covered = true
precision = 2
exclude_lines = [
"pragma: no cover",
# Don't complain about missing debug-only code:
"def __unicode__",
"def __str__",
"def __repr__",
"if self.debug",
"if TYPE_CHECKING",
# Don't complain if tests don't hit defensive assertion code:
"raise AssertionError",
"raise NotImplementedError",
# Don't complain if non-runnable code isn't run:
"if 0:",
"if __name__ == .__main__.:",
"pragma: no cover",
# Don't complain about missing debug-only code:
"def __unicode__",
"def __str__",
"def __repr__",
"if self.debug",
"if TYPE_CHECKING",
# Don't complain if tests don't hit defensive assertion code:
"raise AssertionError",
"raise NotImplementedError",
# Don't complain if non-runnable code isn't run:
"if 0:",
"if __name__ == .__main__.:",
]
show_missing = true
@ -69,20 +64,20 @@ good-names = ["pk", "id", "i", "j", "k", "_", "bar"]
[tool.pylint.master]
disable = [
"arguments-differ",
"locally-disabled",
"too-many-ancestors",
"too-few-public-methods",
"import-outside-toplevel",
"signature-differs",
"similarities",
"cyclic-import",
"protected-access",
"unused-argument",
"raise-missing-from",
"fixme",
# To preserve django's translation function we need to use %-formatting
"consider-using-f-string",
"arguments-differ",
"locally-disabled",
"too-many-ancestors",
"too-few-public-methods",
"import-outside-toplevel",
"signature-differs",
"similarities",
"cyclic-import",
"protected-access",
"unused-argument",
"raise-missing-from",
"fixme",
# To preserve django's translation function we need to use %-formatting
"consider-using-f-string",
]
load-plugins = ["pylint_django", "pylint.extensions.bad_builtin"]
@ -104,18 +99,17 @@ python_files = ["tests.py", "test_*.py", "*_tests.py"]
junit_family = "xunit2"
addopts = "-p no:celery --junitxml=unittest.xml"
filterwarnings = [
"ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning",
"ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning",
"ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning",
"ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning",
]
[tool.poetry]
name = "authentik"
version = "2023.4.1"
version = "2023.4.0"
description = ""
authors = ["authentik Team <hello@goauthentik.io>"]
[tool.poetry.dependencies]
argon2-cffi = "*"
celery = "*"
channels = { version = "*", extras = ["daphne"] }
channels-redis = "*"
@ -182,10 +176,8 @@ pylint-django = "*"
pyrad = "*"
pytest = "*"
pytest-django = "*"
pytest-github-actions-annotate-failures = "*"
pytest-randomly = "*"
requests-mock = "*"
ruff = "*"
selenium = "*"
[build-system]

View File

@ -19,7 +19,7 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
COPY --from=builder /go/radius /
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "/radius", "healthcheck" ]
HEALTHCHECK --interval=5s --retries=20 --start-period=3s CMD [ "wget", "--spider", "http://localhost:9300/outpost.goauthentik.io/ping" ]
EXPOSE 1812/udp 9300

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2023.4.1
version: 2023.4.0
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@ -26351,7 +26351,6 @@ components:
- authentik.tenants
- authentik.blueprints
- authentik.core
- authentik.enterprise
type: string
description: |-
* `authentik.admin` - authentik Admin
@ -26401,7 +26400,6 @@ components:
* `authentik.tenants` - authentik Tenants
* `authentik.blueprints` - authentik Blueprints
* `authentik.core` - authentik Core
* `authentik.enterprise` - authentik Enterprise
AppleChallengeResponseRequest:
type: object
description: Pseudo class for plex response
@ -27710,14 +27708,12 @@ components:
- can_geo_ip
- can_impersonate
- can_debug
- is_enterprise
type: string
description: |-
* `can_save_media` - Can Save Media
* `can_geo_ip` - Can Geo Ip
* `can_impersonate` - Can Impersonate
* `can_debug` - Can Debug
* `is_enterprise` - Is Enterprise
CaptchaChallenge:
type: object
description: Site public key
@ -29118,7 +29114,6 @@ components:
* `authentik.tenants` - authentik Tenants
* `authentik.blueprints` - authentik Blueprints
* `authentik.core` - authentik Core
* `authentik.enterprise` - authentik Enterprise
required:
- bound_to
- component
@ -29228,7 +29223,6 @@ components:
* `authentik.tenants` - authentik Tenants
* `authentik.blueprints` - authentik Blueprints
* `authentik.core` - authentik Core
* `authentik.enterprise` - authentik Enterprise
required:
- name
EventRequest:
@ -35914,7 +35908,6 @@ components:
* `authentik.tenants` - authentik Tenants
* `authentik.blueprints` - authentik Blueprints
* `authentik.core` - authentik Core
* `authentik.enterprise` - authentik Enterprise
PatchedEventRequest:
type: object
description: Event Serializer
@ -36869,14 +36862,8 @@ components:
type: boolean
placeholder:
type: string
description: Optionally provide a short hint that describes the expected
input value. When creating a fixed choice field, enable interpreting as
expression and return a list to return multiple choices.
initial_value:
type: string
description: Optionally pre-fill the input with an initial value. When creating
a fixed choice field, enable interpreting as expression and return a list
to return multiple default choices.
description: When creating a Radio Button Group or Dropdown, enable interpreting
as expression and return a list to return multiple choices.
order:
type: integer
maximum: 2147483647
@ -36889,8 +36876,6 @@ components:
type: string
placeholder_expression:
type: boolean
initial_value_expression:
type: boolean
PatchedPromptStageRequest:
type: object
description: PromptStage Serializer
@ -38049,14 +38034,8 @@ components:
type: boolean
placeholder:
type: string
description: Optionally provide a short hint that describes the expected
input value. When creating a fixed choice field, enable interpreting as
expression and return a list to return multiple choices.
initial_value:
type: string
description: Optionally pre-fill the input with an initial value. When creating
a fixed choice field, enable interpreting as expression and return a list
to return multiple default choices.
description: When creating a Radio Button Group or Dropdown, enable interpreting
as expression and return a list to return multiple choices.
order:
type: integer
maximum: 2147483647
@ -38069,8 +38048,6 @@ components:
type: string
placeholder_expression:
type: boolean
initial_value_expression:
type: boolean
required:
- field_key
- label
@ -38132,14 +38109,8 @@ components:
type: boolean
placeholder:
type: string
description: Optionally provide a short hint that describes the expected
input value. When creating a fixed choice field, enable interpreting as
expression and return a list to return multiple choices.
initial_value:
type: string
description: Optionally pre-fill the input with an initial value. When creating
a fixed choice field, enable interpreting as expression and return a list
to return multiple default choices.
description: When creating a Radio Button Group or Dropdown, enable interpreting
as expression and return a list to return multiple choices.
order:
type: integer
maximum: 2147483647
@ -38152,8 +38123,6 @@ components:
type: string
placeholder_expression:
type: boolean
initial_value_expression:
type: boolean
required:
- field_key
- label
@ -40298,8 +40267,6 @@ components:
type: boolean
placeholder:
type: string
initial_value:
type: string
order:
type: integer
sub_text:
@ -40312,7 +40279,6 @@ components:
required:
- choices
- field_key
- initial_value
- label
- order
- placeholder

View File

@ -3,7 +3,7 @@ from yaml import safe_dump
from authentik.lib.generators import generate_id
with open("local.env.yml", "w", encoding="utf-8") as _config:
with open("local.env.yml", "w") as _config:
safe_dump(
{
"log_level": "debug",

View File

@ -229,6 +229,12 @@ class TestProviderLDAP(SeleniumTestCase):
"homeDirectory": [
f"/home/{o_user.username}",
],
# Old fields for backwards compatibility
"goauthentik.io/ldap/active": ["true"],
"goauthentik.io/ldap/superuser": ["false"],
"goauthentik.io/user/override-ips": ["true"],
"goauthentik.io/user/service-account": ["true"],
# End old fields
"ak-active": ["true"],
"ak-superuser": ["false"],
"goauthentikio-user-override-ips": ["true"],
@ -258,6 +264,12 @@ class TestProviderLDAP(SeleniumTestCase):
"homeDirectory": [
f"/home/{embedded_account.username}",
],
# Old fields for backwards compatibility
"goauthentik.io/ldap/active": ["true"],
"goauthentik.io/ldap/superuser": ["false"],
"goauthentik.io/user/override-ips": ["true"],
"goauthentik.io/user/service-account": ["true"],
# End old fields
"ak-active": ["true"],
"ak-superuser": ["false"],
"goauthentikio-user-override-ips": ["true"],
@ -290,6 +302,10 @@ class TestProviderLDAP(SeleniumTestCase):
"homeDirectory": [
f"/home/{self.user.username}",
],
# Old fields for backwards compatibility
"goauthentik.io/ldap/active": ["true"],
"goauthentik.io/ldap/superuser": ["true"],
# End old fields
"ak-active": ["true"],
"ak-superuser": ["true"],
"extraAttribute": ["bar"],

View File

@ -6,6 +6,7 @@ from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -40,9 +41,14 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
sleep(1)
client: DockerClient = from_env()
container = client.containers.run(
image="ghcr.io/beryju/oidc-test-client:1.3",
image="ghcr.io/beryju/oidc-test-client:v1",
detach=True,
network_mode="host",
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:9009/health"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"OIDC_CLIENT_ID": self.client_id,
"OIDC_CLIENT_SECRET": self.client_secret,

View File

@ -6,6 +6,7 @@ from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -40,9 +41,14 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
sleep(1)
client: DockerClient = from_env()
container = client.containers.run(
image="ghcr.io/beryju/oidc-test-client:1.3",
image="ghcr.io/beryju/oidc-test-client:v1",
detach=True,
network_mode="host",
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:9009/health"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"OIDC_CLIENT_ID": self.client_id,
"OIDC_CLIENT_SECRET": self.client_secret,

View File

@ -11,6 +11,7 @@ from docker.client import DockerClient, from_env
from docker.models.containers import Container
from selenium.webdriver.common.by import By
from authentik import __version__
from authentik.blueprints.tests import apply_blueprint, reconcile_app
from authentik.core.models import Application
from authentik.flows.models import Flow

View File

@ -6,6 +6,7 @@ from unittest.case import skipUnless
from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types import Healthcheck
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
@ -39,9 +40,14 @@ class TestProviderSAML(SeleniumTestCase):
if force_post:
metadata_url += f"&force_binding={SAML_BINDING_POST}"
container = client.containers.run(
image="ghcr.io/beryju/saml-test-sp:1.1",
image="ghcr.io/beryju/saml-test-sp:latest",
detach=True,
network_mode="host",
healthcheck=Healthcheck(
test=["CMD", "wget", "--spider", "http://localhost:9009/health"],
interval=5 * 100 * 1000000,
start_period=1 * 100 * 1000000,
),
environment={
"SP_ENTITY_ID": provider.issuer,
"SP_SSO_BINDING": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",

View File

@ -3,7 +3,6 @@ import json
import os
from functools import lru_cache, wraps
from os import environ
from sys import stderr
from time import sleep
from typing import Any, Callable, Optional
@ -29,7 +28,6 @@ from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
RETRIES = int(environ.get("RETRIES", "3"))
IS_CI = "CI" in environ
def get_docker_tag() -> str:
@ -51,8 +49,6 @@ class SeleniumTestCase(StaticLiveServerTestCase):
user: User
def setUp(self):
if IS_CI:
print("::group::authentik Logs", file=stderr)
super().setUp()
# pylint: disable=invalid-name
self.maxDiff = None
@ -95,12 +91,8 @@ class SeleniumTestCase(StaticLiveServerTestCase):
def output_container_logs(self, container: Optional[Container] = None):
"""Output the container logs to our STDOUT"""
_container = container or self.container
if IS_CI:
print(f"::group::Container logs - {_container.image.tags[0]}")
for log in _container.logs().decode().split("\n"):
print(log)
if IS_CI:
print("::endgroup::")
self.logger.info(log, source="container", container=_container.image.tags[0])
def get_container_specs(self) -> Optional[dict[str, Any]]:
"""Optionally get container specs which will launched on setup, wait for the container to
@ -126,19 +118,15 @@ class SeleniumTestCase(StaticLiveServerTestCase):
raise ValueError(f"Webdriver failed after {RETRIES}.")
def tearDown(self):
super().tearDown()
if IS_CI:
print("::endgroup::", file=stderr)
if IS_CI:
print("::group::Browser logs")
self.logger.debug("--------browser logs")
for line in self.driver.get_log("browser"):
print(line["message"])
if IS_CI:
print("::endgroup::")
self.logger.debug(line["message"], source=line["source"], level=line["level"])
self.logger.debug("--------end browser logs")
if self.container:
self.output_container_logs()
self.container.kill()
self.driver.quit()
super().tearDown()
def wait_for_url(self, desired_url):
"""Wait until URL is `desired_url`."""

View File

@ -9,6 +9,7 @@ from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types.healthcheck import Healthcheck
from authentik import __version__
from authentik.core.tests.utils import create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.outposts.controllers.docker import DockerController

View File

@ -9,6 +9,7 @@ from docker import DockerClient, from_env
from docker.models.containers import Container
from docker.types.healthcheck import Healthcheck
from authentik import __version__
from authentik.core.tests.utils import create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.outposts.models import (

255
web/package-lock.json generated
View File

@ -15,14 +15,14 @@
"@babel/preset-env": "^7.21.4",
"@babel/preset-typescript": "^7.21.4",
"@codemirror/lang-html": "^6.4.3",
"@codemirror/lang-javascript": "^6.1.7",
"@codemirror/lang-javascript": "^6.1.6",
"@codemirror/lang-python": "^6.1.2",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/legacy-modes": "^6.3.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.2.1",
"@codemirror/theme-one-dark": "^6.1.1",
"@formatjs/intl-listformat": "^7.1.9",
"@fortawesome/fontawesome-free": "^6.4.0",
"@goauthentik/api": "^2023.4.1-1681914191",
"@goauthentik/api": "^2023.3.1-1680447184",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.17.2",
@ -35,15 +35,15 @@
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.1.0",
"@sentry/browser": "^7.49.0",
"@sentry/tracing": "^7.49.0",
"@sentry/browser": "^7.47.0",
"@sentry/tracing": "^7.47.0",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.7",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"@webcomponents/webcomponentsjs": "^2.8.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
@ -54,16 +54,16 @@
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.30.1",
"country-flag-icons": "^1.5.7",
"eslint": "^8.39.0",
"eslint": "^8.38.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-lit": "^1.8.3",
"eslint-plugin-lit": "^1.8.2",
"fuse.js": "^6.6.2",
"lit": "^2.7.2",
"mermaid": "^10.1.0",
"moment": "^2.29.4",
"prettier": "^2.8.8",
"pyright": "^1.1.304",
"prettier": "^2.8.7",
"pyright": "^1.1.303",
"rapidoc": "^9.3.4",
"rollup": "^2.79.1",
"rollup-plugin-copy": "^3.4.0",
@ -1788,9 +1788,9 @@
}
},
"node_modules/@codemirror/lang-javascript": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.7.tgz",
"integrity": "sha512-KXKqxlZ4W6t5I7i2ScmITUD3f/F5Cllk3kj0De9P9mFeYVfhOVOWuDLgYiLpk357u7Xh4dhqjJAnsNPPoTLghQ==",
"version": "6.1.6",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.6.tgz",
"integrity": "sha512-TTK28z+vJQY9GAefLTDDptI2LMqMfAiuTpt8s9SsNwocjVQ1v9yTzfReMf1hYhspQCdhfa7fdKnQJ78mKe/bHQ==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
@ -1870,9 +1870,9 @@
"integrity": "sha512-g+3OJuRylV5qsXuuhrc6Cvs1NQluNioepYMM2fhnpYkNk7NgX+j0AFuevKSVKzTDmDyt9+Puju+zPdHNECzCNQ=="
},
"node_modules/@codemirror/theme-one-dark": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.1.tgz",
"integrity": "sha512-+CfzmScfJuD6uDF5bHJkAjWTQ2QAAHxODCPxUEgcImDYcJLT+4l5vLnBHmDVv46kCC5uUJGMrBJct2Z6JbvqyQ==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@ -1982,9 +1982,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
"integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@ -1999,9 +1999,9 @@
}
},
"node_modules/@formatjs/intl-listformat": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.2.1.tgz",
"integrity": "sha512-fRJFWLrGa7d25I4JSxNjKX29oXGcIXx8fJjgURnvs2C3ijS4gurUgFrUwLbv/2KfPfyJ5g567pz2INelNJZBdw==",
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.1.9.tgz",
"integrity": "sha512-5YikxwRqRXTVWVujhswDOTCq6gs+m9IcNbNZLa6FLtyBStAjEsuE2vAU+lPsbz9ZTST57D5fodjIh2JXT6sMWQ==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.14.3",
"@formatjs/intl-localematcher": "0.2.32",
@ -2026,9 +2026,9 @@
}
},
"node_modules/@goauthentik/api": {
"version": "2023.4.1-1681914191",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.4.1-1681914191.tgz",
"integrity": "sha512-8SOx9DIcghKQdXJgerQehiIX3588Tn6SjwBdsi9OsI0yFdSLfJpIBVuMOmW3vdGpjVThSaMsK91KKRo4VqpcUw=="
"version": "2023.3.1-1680447184",
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.3.1-1680447184.tgz",
"integrity": "sha512-b4L7TUb8vkEDnPGpDTicdzEBB6O47KjOVHMWTmn3QzNLj2BCIVx/AF+nM58LvM3eXyrtB203pMX1I1W5Hv9o1g=="
},
"node_modules/@hcaptcha/types": {
"version": "1.0.3",
@ -2949,13 +2949,13 @@
}
},
"node_modules/@sentry-internal/tracing": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.49.0.tgz",
"integrity": "sha512-ESh3+ZneQk/3HESTUmIPNrW5GVPu/HrRJU+eAJJto74vm+6vP7zDn2YV2gJ1w18O/37nc7W/bVCgZJlhZ3cwew==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.47.0.tgz",
"integrity": "sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==",
"dependencies": {
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2968,15 +2968,15 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/browser": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.49.0.tgz",
"integrity": "sha512-x2DekKkQoY7/dhBzE4J25mdQ978NtPBTVQb+uZqlF/t5mp4K44TAszmPqy8lC/CmVHkp7qcpRGSCIzeboUL4KA==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.47.0.tgz",
"integrity": "sha512-L0t07kS/G1UGVZ9fpD6HLuaX8vVBqAGWgu+1uweXthYozu/N7ZAsakjU/Ozu6FSXj1mO3NOJZhOn/goIZLSj5A==",
"dependencies": {
"@sentry-internal/tracing": "7.49.0",
"@sentry/core": "7.49.0",
"@sentry/replay": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"@sentry-internal/tracing": "7.47.0",
"@sentry/core": "7.47.0",
"@sentry/replay": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2989,12 +2989,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/core": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.49.0.tgz",
"integrity": "sha512-AlSnCYgfEbvK8pkNluUkmdW/cD9UpvOVCa+ERQswXNRkAv5aDGCL6Ihv6fnIajE++BYuwZh0+HwZUBVKTFzoZg==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.47.0.tgz",
"integrity": "sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==",
"dependencies": {
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3007,43 +3007,43 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/replay": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.49.0.tgz",
"integrity": "sha512-UY3bHoBDPOu4Dpq3m3oxNjLrq09NiFVYUfrTN4QOq1Am2SA04XbuCj/YZ+jNVy/NrFtoz9cTovK6oQbNw53jog==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.47.0.tgz",
"integrity": "sha512-BFpVZVmwlezZ83y0L43TCTJY142Fxh+z+qZSwTag5HlhmIpBKw/WKg06ajOhrYJbCBkhHmeOvyKkxX0jnc39ZA==",
"dependencies": {
"@sentry/core": "7.49.0",
"@sentry/types": "7.49.0",
"@sentry/utils": "7.49.0"
"@sentry/core": "7.47.0",
"@sentry/types": "7.47.0",
"@sentry/utils": "7.47.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@sentry/tracing": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.49.0.tgz",
"integrity": "sha512-RtyTt1DvX7s1M2ca9qnevOkuwn8HjbKXrSVHtMbQYoT3uGvjT8Pm71D5WtWMWH2QLpFgcqQq/1ifZBUAG4Y7qA==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.47.0.tgz",
"integrity": "sha512-hJCpKdekwaFNbCVXxfCz5IxfSEJIKnkPmRSVHITOm5VhKwq2e5kmy4Rn6bzSETwJFSDE8LGbR/3eSfGTqw37XA==",
"dependencies": {
"@sentry-internal/tracing": "7.49.0"
"@sentry-internal/tracing": "7.47.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/types": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.49.0.tgz",
"integrity": "sha512-9yXXh7iv76+O6h2ONUVx0wsL1auqJFWez62mTjWk4350SgMmWp/zUkBxnVXhmcYqscz/CepC+Loz9vITLXtgxg==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.47.0.tgz",
"integrity": "sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/utils": {
"version": "7.49.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.49.0.tgz",
"integrity": "sha512-JdC9yGnOgev4ISJVwmIoFsk8Zx0psDZJAj2DV7x4wMZsO6QK+YjC7G3mUED/S5D5lsrkBZ/3uvQQhr8DQI4UcQ==",
"version": "7.47.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.47.0.tgz",
"integrity": "sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==",
"dependencies": {
"@sentry/types": "7.49.0",
"@sentry/types": "7.47.0",
"tslib": "^1.9.3"
},
"engines": {
@ -3379,14 +3379,14 @@
"integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz",
"integrity": "sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz",
"integrity": "sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==",
"dependencies": {
"@eslint-community/regexpp": "^4.4.0",
"@typescript-eslint/scope-manager": "5.59.0",
"@typescript-eslint/type-utils": "5.59.0",
"@typescript-eslint/utils": "5.59.0",
"@typescript-eslint/scope-manager": "5.58.0",
"@typescript-eslint/type-utils": "5.58.0",
"@typescript-eslint/utils": "5.58.0",
"debug": "^4.3.4",
"grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
@ -3426,13 +3426,13 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.0.tgz",
"integrity": "sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.58.0.tgz",
"integrity": "sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.59.0",
"@typescript-eslint/types": "5.59.0",
"@typescript-eslint/typescript-estree": "5.59.0",
"@typescript-eslint/scope-manager": "5.58.0",
"@typescript-eslint/types": "5.58.0",
"@typescript-eslint/typescript-estree": "5.58.0",
"debug": "^4.3.4"
},
"engines": {
@ -3452,12 +3452,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.0.tgz",
"integrity": "sha512-tsoldKaMh7izN6BvkK6zRMINj4Z2d6gGhO2UsI8zGZY3XhLq1DndP3Ycjhi1JwdwPRwtLMW4EFPgpuKhbCGOvQ==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz",
"integrity": "sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==",
"dependencies": {
"@typescript-eslint/types": "5.59.0",
"@typescript-eslint/visitor-keys": "5.59.0"
"@typescript-eslint/types": "5.58.0",
"@typescript-eslint/visitor-keys": "5.58.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -3468,12 +3468,12 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz",
"integrity": "sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz",
"integrity": "sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==",
"dependencies": {
"@typescript-eslint/typescript-estree": "5.59.0",
"@typescript-eslint/utils": "5.59.0",
"@typescript-eslint/typescript-estree": "5.58.0",
"@typescript-eslint/utils": "5.58.0",
"debug": "^4.3.4",
"tsutils": "^3.21.0"
},
@ -3494,9 +3494,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.0.tgz",
"integrity": "sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.58.0.tgz",
"integrity": "sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@ -3506,12 +3506,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz",
"integrity": "sha512-sUNnktjmI8DyGzPdZ8dRwW741zopGxltGs/SAPgGL/AAgDpiLsCFLcMNSpbfXfmnNeHmK9h3wGmCkGRGAoUZAg==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz",
"integrity": "sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==",
"dependencies": {
"@typescript-eslint/types": "5.59.0",
"@typescript-eslint/visitor-keys": "5.59.0",
"@typescript-eslint/types": "5.58.0",
"@typescript-eslint/visitor-keys": "5.58.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@ -3532,9 +3532,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
"integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz",
"integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@ -3546,16 +3546,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.0.tgz",
"integrity": "sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz",
"integrity": "sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.59.0",
"@typescript-eslint/types": "5.59.0",
"@typescript-eslint/typescript-estree": "5.59.0",
"@typescript-eslint/scope-manager": "5.58.0",
"@typescript-eslint/types": "5.58.0",
"@typescript-eslint/typescript-estree": "5.58.0",
"eslint-scope": "^5.1.1",
"semver": "^7.3.7"
},
@ -3571,9 +3571,9 @@
}
},
"node_modules/@typescript-eslint/utils/node_modules/semver": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz",
"integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==",
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz",
"integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@ -3585,11 +3585,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.59.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz",
"integrity": "sha512-qZ3iXxQhanchCeaExlKPV3gDQFxMUmU35xfd5eCXB6+kUw1TUAbIy2n7QIrwz9s98DQLzNWyHp61fY0da4ZcbA==",
"version": "5.58.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz",
"integrity": "sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==",
"dependencies": {
"@typescript-eslint/types": "5.59.0",
"@typescript-eslint/types": "5.58.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@ -5255,14 +5255,14 @@
}
},
"node_modules/eslint": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
"integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.2",
"@eslint/js": "8.39.0",
"@eslint/js": "8.38.0",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -5272,7 +5272,7 @@
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.2.0",
"eslint-scope": "^7.1.1",
"eslint-visitor-keys": "^3.4.0",
"espree": "^9.5.1",
"esquery": "^1.4.2",
@ -5330,9 +5330,9 @@
}
},
"node_modules/eslint-plugin-lit": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.8.3.tgz",
"integrity": "sha512-wmeYfBnWPUChbdZagOhG519gaWz9Q7OGT/nCx3YVHuCCrW9q9u0p/IQueQeoaMojUqOSgM/22oSDOaBruYGqag==",
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.8.2.tgz",
"integrity": "sha512-4mOGcSRNEPMh7AN2F7Iy6no36nuFgyYOsnTRhFw1k8xyy1Zm6QOp788ywDvJqy+eelFbLPBhq20Qr55a887Dmw==",
"dependencies": {
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
@ -5425,18 +5425,15 @@
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
"integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/estraverse": {
@ -8120,9 +8117,9 @@
}
},
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
"integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
"bin": {
"prettier": "bin-prettier.js"
},
@ -8219,9 +8216,9 @@
}
},
"node_modules/pyright": {
"version": "1.1.304",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.304.tgz",
"integrity": "sha512-b7q53RytKcu/JIzBDfT4po1Uj1eyjXP4wHcWEOXNkWt8+fS8X6lZhRjPe5yzfPvh/2heTKcIEwY+J/VuMobuyw==",
"version": "1.1.303",
"resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.303.tgz",
"integrity": "sha512-uwJdp3KRidmpIgKKQtNL17F2noF2PsK6bxqd7WWrQE1AfCHI0j53B9lro63S/9oWWu18xfgTI4IqKO5S1srxhA==",
"bin": {
"pyright": "index.js",
"pyright-langserver": "langserver.index.js"

View File

@ -59,14 +59,14 @@
"@babel/preset-env": "^7.21.4",
"@babel/preset-typescript": "^7.21.4",
"@codemirror/lang-html": "^6.4.3",
"@codemirror/lang-javascript": "^6.1.7",
"@codemirror/lang-javascript": "^6.1.6",
"@codemirror/lang-python": "^6.1.2",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/legacy-modes": "^6.3.2",
"@codemirror/theme-one-dark": "^6.1.2",
"@formatjs/intl-listformat": "^7.2.1",
"@codemirror/theme-one-dark": "^6.1.1",
"@formatjs/intl-listformat": "^7.1.9",
"@fortawesome/fontawesome-free": "^6.4.0",
"@goauthentik/api": "^2023.4.1-1681914191",
"@goauthentik/api": "^2023.3.1-1680447184",
"@hcaptcha/types": "^1.0.3",
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
"@lingui/cli": "^3.17.2",
@ -79,15 +79,15 @@
"@rollup/plugin-node-resolve": "^15.0.2",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.1.0",
"@sentry/browser": "^7.49.0",
"@sentry/tracing": "^7.49.0",
"@sentry/browser": "^7.47.0",
"@sentry/tracing": "^7.47.0",
"@squoosh/cli": "^0.7.3",
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
"@types/chart.js": "^2.9.37",
"@types/codemirror": "5.60.7",
"@types/grecaptcha": "^3.0.4",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"@typescript-eslint/eslint-plugin": "^5.58.0",
"@typescript-eslint/parser": "^5.58.0",
"@webcomponents/webcomponentsjs": "^2.8.0",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-tsconfig-paths": "^1.0.3",
@ -98,16 +98,16 @@
"construct-style-sheets-polyfill": "^3.1.0",
"core-js": "^3.30.1",
"country-flag-icons": "^1.5.7",
"eslint": "^8.39.0",
"eslint": "^8.38.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-custom-elements": "0.0.8",
"eslint-plugin-lit": "^1.8.3",
"eslint-plugin-lit": "^1.8.2",
"fuse.js": "^6.6.2",
"lit": "^2.7.2",
"mermaid": "^10.1.0",
"moment": "^2.29.4",
"prettier": "^2.8.8",
"pyright": "^1.1.304",
"prettier": "^2.8.7",
"pyright": "^1.1.303",
"rapidoc": "^9.3.4",
"rollup": "^2.79.1",
"rollup-plugin-copy": "^3.4.0",

View File

@ -60,7 +60,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
});
}
const c = await config();
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
const icon = this.getFormFiles()["metaIcon"];
if (icon || this.clearIcon) {
await new CoreApi(DEFAULT_CONFIG).coreApplicationsSetIconCreate({
@ -195,7 +195,7 @@ export class ApplicationForm extends ModelForm<Application, string> {
${t`If checked, the launch URL will open in a new browser tab or window from the user's application library.`}
</p>
</ak-form-element-horizontal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
? html`<ak-form-element-horizontal label=${t`Icon`} name="metaIcon">
<input type="file" value="" class="pf-c-form-control" />
${this.instance?.metaIcon

View File

@ -126,11 +126,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
row(item: BlueprintInstance): TemplateResult[] {
let description = undefined;
const descKey = "blueprints.goauthentik.io/description";
if (
item.metadata &&
item.metadata.labels &&
Object.hasOwn(item.metadata?.labels, descKey)
) {
if (Object.hasOwn(item.metadata?.labels, descKey)) {
description = item.metadata?.labels[descKey];
}
return [

View File

@ -95,25 +95,17 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
if (item.managed && item.managed.startsWith("goauthentik.io/crypto/discovered")) {
managedSubText = t`Managed by authentik (Discovered)`;
}
let color = PFColor.Green;
if (item.certExpiry) {
const now = new Date();
const inAMonth = new Date();
inAMonth.setDate(inAMonth.getDate() + 30);
if (item.certExpiry <= inAMonth) {
color = PFColor.Orange;
}
if (item.certExpiry <= now) {
color = PFColor.Red;
}
}
return [
html`<div>${item.name}</div>
${item.managed ? html`<small>${managedSubText}</small>` : html``}`,
html`<ak-label color=${item.privateKeyAvailable ? PFColor.Green : PFColor.Grey}>
${item.privateKeyAvailable ? t`Yes (${item.privateKeyType?.toUpperCase()})` : t`No`}
</ak-label>`,
html`<ak-label color=${color}> ${item.certExpiry?.toLocaleString()} </ak-label>`,
html`<ak-label
color=${item.certExpiry || new Date() > new Date() ? PFColor.Green : PFColor.Orange}
>
${item.certExpiry?.toLocaleString()}
</ak-label>`,
html`<ak-forms-modal>
<span slot="submit"> ${t`Update`} </span>
<span slot="header"> ${t`Update Certificate-Key Pair`} </span>

View File

@ -56,7 +56,7 @@ export class FlowForm extends ModelForm<Flow, string> {
});
}
const c = await config();
if (c.capabilities.includes(CapabilitiesEnum.CanSaveMedia)) {
if (c.capabilities.includes(CapabilitiesEnum.SaveMedia)) {
const icon = this.getFormFiles()["background"];
if (icon || this.clearBackground) {
await new FlowsApi(DEFAULT_CONFIG).flowsInstancesSetBackgroundCreate({
@ -315,7 +315,7 @@ export class FlowForm extends ModelForm<Flow, string> {
</option>
</select>
</ak-form-element-horizontal>
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanSaveMedia)
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.SaveMedia)
? html`<ak-form-element-horizontal label=${t`Background`} name="background">
<input type="file" value="" class="pf-c-form-control" />
${this.instance?.background

View File

@ -50,6 +50,9 @@ export class FlowListPage extends TablePage<Flow> {
groupBy(items: Flow[]): [string, Flow[]][] {
return groupBy(items, (flow) => {
if (!flow.designation) {
return "";
}
return DesignationToLabel(flow.designation);
});
}

View File

@ -47,12 +47,11 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
return t`Successfully created binding.`;
}
}
send(data: FlowStageBinding): Promise<unknown> {
if (this.instance?.pk) {
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsPartialUpdate({
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsUpdate({
fsbUuid: this.instance.pk,
patchedFlowStageBindingRequest: data,
flowStageBindingRequest: data,
});
} else {
if (this.targetPk) {

View File

@ -85,7 +85,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
html`<ak-label color=${item.local ? PFColor.Grey : PFColor.Green}>
${item.local ? t`Yes` : t`No`}
</ak-label>`,
html`${itemState?.healthy
html`${itemState.healthy
? html`<ak-label color=${PFColor.Green}>${ifDefined(itemState.version)}</ak-label>`
: html`<ak-label color=${PFColor.Red}>${t`Unhealthy`}</ak-label>`}`,
html` <ak-forms-modal>

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