Merge branch 'master' into inbuilt-proxy

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

# Conflicts:
#	internal/constants/constants.go
#	outpost/pkg/version.go
This commit is contained in:
Jens Langhammer 2021-07-02 16:23:30 +02:00
commit 3dc9e247d5
42 changed files with 481 additions and 227 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.6.2 current_version = 2021.6.3
tag = True tag = True
commit = True commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*) parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
@ -21,6 +21,8 @@ values =
[bumpversion:file:docker-compose.yml] [bumpversion:file:docker-compose.yml]
[bumpversion:file:schema.yml]
[bumpversion:file:.github/workflows/release.yml] [bumpversion:file:.github/workflows/release.yml]
[bumpversion:file:authentik/__init__.py] [bumpversion:file:authentik/__init__.py]

View File

@ -33,14 +33,14 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik:2021.6.2, beryju/authentik:2021.6.3,
beryju/authentik:latest, beryju/authentik:latest,
ghcr.io/goauthentik/server:2021.6.2, ghcr.io/goauthentik/server:2021.6.3,
ghcr.io/goauthentik/server:latest ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
context: . context: .
- name: Building Docker Image (stable) - name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.6.2', 'rc') }} if: ${{ github.event_name == 'release' && !contains('2021.6.3', 'rc') }}
run: | run: |
docker pull beryju/authentik:latest docker pull beryju/authentik:latest
docker tag beryju/authentik:latest beryju/authentik:stable docker tag beryju/authentik:latest beryju/authentik:stable
@ -75,14 +75,14 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-proxy:2021.6.2, beryju/authentik-proxy:2021.6.3,
beryju/authentik-proxy:latest, beryju/authentik-proxy:latest,
ghcr.io/goauthentik/proxy:2021.6.2, ghcr.io/goauthentik/proxy:2021.6.3,
ghcr.io/goauthentik/proxy:latest ghcr.io/goauthentik/proxy:latest
file: proxy.Dockerfile file: proxy.Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable) - name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.6.2', 'rc') }} if: ${{ github.event_name == 'release' && !contains('2021.6.3', 'rc') }}
run: | run: |
docker pull beryju/authentik-proxy:latest docker pull beryju/authentik-proxy:latest
docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable
@ -117,14 +117,14 @@ jobs:
with: with:
push: ${{ github.event_name == 'release' }} push: ${{ github.event_name == 'release' }}
tags: | tags: |
beryju/authentik-ldap:2021.6.2, beryju/authentik-ldap:2021.6.3,
beryju/authentik-ldap:latest, beryju/authentik-ldap:latest,
ghcr.io/goauthentik/ldap:2021.6.2, ghcr.io/goauthentik/ldap:2021.6.3,
ghcr.io/goauthentik/ldap:latest ghcr.io/goauthentik/ldap:latest
file: ldap.Dockerfile file: ldap.Dockerfile
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable) - name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2021.6.2', 'rc') }} if: ${{ github.event_name == 'release' && !contains('2021.6.3', 'rc') }}
run: | run: |
docker pull beryju/authentik-ldap:latest docker pull beryju/authentik-ldap:latest
docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable
@ -157,7 +157,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Setup Node.js environment - name: Setup Node.js environment
uses: actions/setup-node@v2.1.5 uses: actions/setup-node@v2.2.0
with: with:
node-version: 12.x node-version: 12.x
- name: Build web api client and web ui - name: Build web api client and web ui
@ -176,6 +176,7 @@ jobs:
SENTRY_PROJECT: authentik SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
version: authentik@2021.6.2 version: authentik@2021.6.3
environment: beryjuorg-prod environment: beryjuorg-prod
sourcemaps: './web/dist' sourcemaps: './web/dist'
finalize: false

128
Pipfile.lock generated
View File

@ -76,11 +76,11 @@
}, },
"asgiref": { "asgiref": {
"hashes": [ "hashes": [
"sha256:05914d0fa65a21711e732adc6572edad6c8da5f1435c3f0c060689ced5e85195", "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
"sha256:d36fa91dd90e3aa3c81a6bd426ccc8fb20bd3d22b0cf14a12800289e9c3e2563" "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==3.4.0" "version": "==3.4.1"
}, },
"async-timeout": { "async-timeout": {
"hashes": [ "hashes": [
@ -122,19 +122,19 @@
}, },
"boto3": { "boto3": {
"hashes": [ "hashes": [
"sha256:6300e9ee9a404038113250bd218e2c4827f5e676efb14e77de2ad2dcb67679bc", "sha256:055f9dc07f95f202a4dc25196a3a9f1e2f137171ee364cf980e4673de75fb529",
"sha256:be4714f0475c1f5183eea09ddbf568ced6fa41b0fc9976f2698b8442e1b17303" "sha256:bc9b278e362ec9b531511a498262297f074c4f5ca9560455919a0af1a4698615"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.17.102" "version": "==1.17.104"
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:2f57f7ceed1598d96cc497aeb45317db5d3b21a5aafea4732d0e561d0fc2a8fa", "sha256:23aa3238c004319f78423eb8cbba2813b62ee64d0e3bab04e0a00e067f99542a",
"sha256:bdf08a4f7f01ead00d386848f089c08270499711447569c18d0db60023619c06" "sha256:95ab472c8254b8d2cfa6d719b433e511fbcf80895b4cd18e4219c1efa0b78270"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.20.102" "version": "==1.20.104"
}, },
"cachetools": { "cachetools": {
"hashes": [ "hashes": [
@ -242,11 +242,11 @@
}, },
"channels-redis": { "channels-redis": {
"hashes": [ "hashes": [
"sha256:18d63f6462a58011740dc8eeb57ea4b31ec220eb551cb71b27de9c6779a549de", "sha256:0a18ce279c15ba79b7985bb12b2d6dd0ac8a14e4ad6952681f4422a4cc4a5ea9",
"sha256:2fb31a63b05373f6402da2e6a91a22b9e66eb8b56626c6bfc93e156c734c5ae6" "sha256:1abd5820ff1ed4ac627f8a219ad389e4c87e52e47a230929a7a474e95dd2c6c2"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.2.0" "version": "==3.3.0"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@ -342,11 +342,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:66c9d8db8cc6fe938a28b7887c1596e42d522e27618562517cc8929eb7e7f296", "sha256:3da05fea54fdec2315b54a563d5b59f3b4e2b1e69c3a5841dda35019c01855cd",
"sha256:ea735cbbbb3b2fba6d4da4784a0043d84c67c92f1fdf15ad6db69900e792c10f" "sha256:c58b5f19c5ae0afe6d75cbdd7df561e6eb929339985dbbda2565e1cabb19a62e"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.2.4" "version": "==3.2.5"
}, },
"django-dbbackup": { "django-dbbackup": {
"git": "https://github.com/django-dbbackup/django-dbbackup.git", "git": "https://github.com/django-dbbackup/django-dbbackup.git",
@ -473,11 +473,11 @@
}, },
"google-auth": { "google-auth": {
"hashes": [ "hashes": [
"sha256:b3a67fa9ba5b768861dacf374c2135eb09fa14a0e40c851c3b8ea7abe6fc8fef", "sha256:9266252e11393943410354cf14a77bcca24dd2ccd9c4e1aef23034fe0fbae630",
"sha256:e34e5f5de5610b202f9b40ebd9f8b27571d5c5537db9afed3a72b2db5a345039" "sha256:c7c215c74348ef24faef2f7b62f6d8e6b38824fe08b1e7b7b09a02d397eda7b3"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.32.0" "version": "==1.32.1"
}, },
"gunicorn": { "gunicorn": {
"hashes": [ "hashes": [
@ -1423,11 +1423,11 @@
}, },
"astroid": { "astroid": {
"hashes": [ "hashes": [
"sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e", "sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892",
"sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975" "sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9"
], ],
"markers": "python_version ~= '3.6'", "markers": "python_version ~= '3.6'",
"version": "==2.5.6" "version": "==2.6.2"
}, },
"attrs": { "attrs": {
"hashes": [ "hashes": [
@ -1671,11 +1671,11 @@
}, },
"pylint": { "pylint": {
"hashes": [ "hashes": [
"sha256:0a049c5d47b629d9070c3932d13bff482b12119b6a241a93bc460b0be16953c8", "sha256:23a1dc8b30459d78e9ff25942c61bb936108ccbe29dd9e71c01dc8274961709a",
"sha256:792b38ff30903884e4a9eab814ee3523731abd3c463f3ba48d7b627e87013484" "sha256:5d46330e6b8886c31b5e3aba5ff48c10f4aa5e76cbf9002c6544306221e63fbc"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.8.3" "version": "==2.9.3"
}, },
"pylint-django": { "pylint-django": {
"hashes": [ "hashes": [
@ -1753,49 +1753,45 @@
}, },
"regex": { "regex": {
"hashes": [ "hashes": [
"sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5", "sha256:0e46c1191b2eb293a6912269ed08b4512e7e241bbf591f97e527492e04c77e93",
"sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79", "sha256:18040755606b0c21281493ec309214bd61e41a170509e5014f41d6a5a586e161",
"sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31", "sha256:1806370b2bef4d4193eebe8ee59a9fd7547836a34917b7badbe6561a8594d9cb",
"sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500", "sha256:1ccbd41dbee3a31e18938096510b7d4ee53aa9fce2ee3dcc8ec82ae264f6acfd",
"sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11", "sha256:1d386402ae7f3c9b107ae5863f7ecccb0167762c82a687ae6526b040feaa5ac6",
"sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14", "sha256:210c359e6ee5b83f7d8c529ba3c75ba405481d50f35a420609b0db827e2e3bb5",
"sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3", "sha256:268fe9dd1deb4a30c8593cabd63f7a241dfdc5bd9dd0233906c718db22cdd49a",
"sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439", "sha256:361be4d311ac995a8c7ad577025a3ae3a538531b1f2cf32efd8b7e5d33a13e5a",
"sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c", "sha256:3f7a92e60930f8fca2623d9e326c173b7cf2c8b7e4fdcf984b75a1d2fb08114d",
"sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82", "sha256:444723ebaeb7fa8125f29c01a31101a3854ac3de293e317944022ae5effa53a4",
"sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711", "sha256:494d0172774dc0beeea984b94c95389143db029575f7ca908edd74469321ea99",
"sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093", "sha256:4b1999ef60c45357598935c12508abf56edbbb9c380df6f336de38a6c3a294ae",
"sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a", "sha256:4fc86b729ab88fe8ac3ec92287df253c64aa71560d76da5acd8a2e245839c629",
"sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb", "sha256:5049d00dbb78f9d166d1c704e93934d42cce0570842bb1a61695123d6b01de09",
"sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8", "sha256:56bef6b414949e2c9acf96cb5d78de8b529c7b99752619494e78dc76f99fd005",
"sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17", "sha256:59845101de68fd5d3a1145df9ea022e85ecd1b49300ea68307ad4302320f6f61",
"sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000", "sha256:6b8b629f93246e507287ee07e26744beaffb4c56ed520576deac8b615bd76012",
"sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d", "sha256:6c72ebb72e64e9bd195cb35a9b9bbfb955fd953b295255b8ae3e4ad4a146b615",
"sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480", "sha256:7743798dfb573d006f1143d745bf17efad39775a5190b347da5d83079646be56",
"sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc", "sha256:78a2a885345a2d60b5e68099e877757d5ed12e46ba1e87507175f14f80892af3",
"sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0", "sha256:849802379a660206277675aa5a5c327f5c910c690649535863ddf329b0ba8c87",
"sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9", "sha256:8cf6728f89b071bd3ab37cb8a0e306f4de897553a0ed07442015ee65fbf53d62",
"sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765", "sha256:a1b6a3f600d6aff97e3f28c34192c9ed93fee293bd96ef327b64adb51a74b2f6",
"sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e", "sha256:a548bb51c4476332ce4139df8e637386730f79a92652a907d12c696b6252b64d",
"sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a", "sha256:a8a5826d8a1b64e2ff9af488cc179e1a4d0f144d11ce486a9f34ea38ccedf4ef",
"sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07", "sha256:b024ee43ee6b310fad5acaee23e6485b21468718cb792a9d1693eecacc3f0b7e",
"sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f", "sha256:b092754c06852e8a8b022004aff56c24b06310189186805800d09313c37ce1f8",
"sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac", "sha256:b1dbeef938281f240347d50f28ae53c4b046a23389cd1fc4acec5ea0eae646a1",
"sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7", "sha256:bf819c5b77ff44accc9a24e31f1f7ceaaf6c960816913ed3ef8443b9d20d81b6",
"sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed", "sha256:c11f2fca544b5e30a0e813023196a63b1cb9869106ef9a26e9dae28bce3e4e26",
"sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968", "sha256:ce269e903b00d1ab4746793e9c50a57eec5d5388681abef074d7b9a65748fca5",
"sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7", "sha256:d0cf2651a8804f6325747c7e55e3be0f90ee2848e25d6b817aa2728d263f9abb",
"sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2", "sha256:e07e92935040c67f49571779d115ecb3e727016d42fb36ee0d8757db4ca12ee0",
"sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4", "sha256:e80d2851109e56420b71f9702ad1646e2f0364528adbf6af85527bc61e49f394",
"sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87", "sha256:ed77b97896312bc2deafe137ca2626e8b63808f5bedb944f73665c68093688a7",
"sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8", "sha256:f32f47fb22c988c0b35756024b61d156e5c4011cb8004aa53d93b03323c45657",
"sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10", "sha256:fdad3122b69cdabdb3da4c2a4107875913ac78dab0117fc73f988ad589c66b66"
"sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29",
"sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605",
"sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6",
"sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"
], ],
"version": "==2021.4.4" "version": "==2021.7.1"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [

View File

@ -1,3 +1,3 @@
"""authentik""" """authentik"""
__version__ = "2021.6.2" __version__ = "2021.6.3"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -5,14 +5,13 @@ from typing import Any, Optional, Type
from urllib.parse import urlencode from urllib.parse import urlencode
from uuid import uuid4 from uuid import uuid4
import django.db.models.options as options
from deepmerge import always_merger from deepmerge import always_merger
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import UserManager as DjangoUserManager from django.contrib.auth.models import UserManager as DjangoUserManager
from django.core import validators from django.core import validators
from django.db import models from django.db import models
from django.db.models import Q, QuerySet from django.db.models import Q, QuerySet, options
from django.http import HttpRequest from django.http import HttpRequest
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.functional import cached_property from django.utils.functional import cached_property

View File

@ -16,11 +16,6 @@ from authentik.crypto.models import CertificateKeyPair
class CertificateBuilder: class CertificateBuilder:
"""Build self-signed certificates""" """Build self-signed certificates"""
__public_key = None
__private_key = None
__builder = None
__certificate = None
common_name: str common_name: str
def __init__(self): def __init__(self):

View File

@ -46,7 +46,7 @@ class NotificationTransportTestSerializer(Serializer):
messages = ListField(child=CharField()) messages = ListField(child=CharField())
def create(self, request: Request) -> Response: def create(self, validated_data: Request) -> Response:
raise NotImplementedError raise NotImplementedError
def update(self, request: Request) -> Response: def update(self, request: Request) -> Response:

View File

@ -27,10 +27,9 @@ class GeoIPDict(TypedDict):
class GeoIPReader: class GeoIPReader:
"""Slim wrapper around GeoIP API""" """Slim wrapper around GeoIP API"""
__reader: Optional[Reader] = None
__last_mtime: float = 0.0
def __init__(self): def __init__(self):
self.__reader: Optional[Reader] = None
self.__last_mtime: float = 0.0
self.__open() self.__open()
def __open(self): def __open(self):

View File

@ -40,15 +40,11 @@ def transaction_rollback():
class FlowImporter: class FlowImporter:
"""Import Flow from json""" """Import Flow from json"""
__import: FlowBundle
__pk_map: dict[Any, Model]
logger: BoundLogger logger: BoundLogger
def __init__(self, json_input: str): def __init__(self, json_input: str):
self.__pk_map: dict[Any, Model] = {}
self.logger = get_logger() self.logger = get_logger()
self.__pk_map = {}
import_dict = loads(json_input) import_dict = loads(json_input)
try: try:
self.__import = from_dict(FlowBundle, import_dict) self.__import = from_dict(FlowBundle, import_dict)

View File

@ -26,10 +26,9 @@ class ConfigLoader:
loaded_file = [] loaded_file = []
__config = {}
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.__config = {}
base_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "../..")) base_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "../.."))
for path in SEARCH_PATHS: for path in SEARCH_PATHS:
# Check if path is relative, and if so join with base_dir # Check if path is relative, and if so join with base_dir

View File

@ -69,7 +69,7 @@ class OutpostConsumer(AuthJsonConsumer):
self.last_uid = self.channel_name self.last_uid = self.channel_name
# pylint: disable=unused-argument # pylint: disable=unused-argument
def disconnect(self, close_code): def disconnect(self, code):
if self.outpost and self.last_uid: if self.outpost and self.last_uid:
state = OutpostState.for_instance_uid(self.outpost, self.last_uid) state = OutpostState.for_instance_uid(self.outpost, self.last_uid)
if self.channel_name in state.channel_ids: if self.channel_name in state.channel_ids:

View File

@ -82,13 +82,13 @@ class PolicyBindingSerializer(ModelSerializer):
"timeout", "timeout",
] ]
def validate(self, data: OrderedDict) -> OrderedDict: def validate(self, attrs: OrderedDict) -> OrderedDict:
"""Check that either policy, group or user is set.""" """Check that either policy, group or user is set."""
count = sum( count = sum(
[ [
bool(data.get("policy", None)), bool(attrs.get("policy", None)),
bool(data.get("group", None)), bool(attrs.get("group", None)),
bool(data.get("user", None)), bool(attrs.get("user", None)),
] ]
) )
invalid = count > 1 invalid = count > 1
@ -97,7 +97,7 @@ class PolicyBindingSerializer(ModelSerializer):
raise ValidationError("Only one of 'policy', 'group' or 'user' can be set.") raise ValidationError("Only one of 'policy', 'group' or 'user' can be set.")
if empty: if empty:
raise ValidationError("One of 'policy', 'group' or 'user' must be set.") raise ValidationError("One of 'policy', 'group' or 'user' must be set.")
return data return attrs
class PolicyBindingViewSet(UsedByMixin, ModelViewSet): class PolicyBindingViewSet(UsedByMixin, ModelViewSet):

View File

@ -62,12 +62,6 @@ class PolicyEngine:
# Allow objects with no policies attached to pass # Allow objects with no policies attached to pass
empty_result: bool empty_result: bool
__pbm: PolicyBindingModel
__cached_policies: list[PolicyResult]
__processes: list[PolicyProcessInfo]
__expected_result_count: int
def __init__( def __init__(
self, pbm: PolicyBindingModel, user: User, request: HttpRequest = None self, pbm: PolicyBindingModel, user: User, request: HttpRequest = None
): ):
@ -83,8 +77,8 @@ class PolicyEngine:
self.request.obj = pbm self.request.obj = pbm
if request: if request:
self.request.set_http_request(request) self.request.set_http_request(request)
self.__cached_policies = [] self.__cached_policies: list[PolicyResult] = []
self.__processes = [] self.__processes: list[PolicyProcessInfo] = []
self.use_cache = True self.use_cache = True
self.__expected_result_count = 0 self.__expected_result_count = 0

View File

@ -278,7 +278,7 @@ class OAuth2Provider(Provider):
"""Guess launch_url based on first redirect_uri""" """Guess launch_url based on first redirect_uri"""
if self.redirect_uris == "": if self.redirect_uris == "":
return None return None
main_url = self.redirect_uris.split("\n")[0] main_url = self.redirect_uris.split("\n", maxsplit=1)[0]
launch_url = urlparse(main_url) launch_url = urlparse(main_url)
return main_url.replace(launch_url.path, "") return main_url.replace(launch_url.path, "")

View File

@ -1,6 +1,7 @@
"""authentik OAuth2 OpenID Userinfo views""" """authentik OAuth2 OpenID Userinfo views"""
from typing import Any, Optional from typing import Any, Optional
from deepmerge import always_merger
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.http.response import HttpResponseBadRequest from django.http.response import HttpResponseBadRequest
from django.views import View from django.views import View
@ -78,7 +79,7 @@ class UserInfoView(View):
) )
continue continue
LOGGER.debug("updated scope", scope=scope) LOGGER.debug("updated scope", scope=scope)
final_claims.update(value) always_merger.merge(final_claims, value)
return final_claims return final_claims
def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:

View File

@ -15,7 +15,7 @@ class MessageConsumer(JsonWebsocketConsumer):
cache.set(f"user_{self.session_key}_messages_{self.channel_name}", True, None) cache.set(f"user_{self.session_key}_messages_{self.channel_name}", True, None)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def disconnect(self, close_code): def disconnect(self, code):
cache.delete(f"user_{self.session_key}_messages_{self.channel_name}") cache.delete(f"user_{self.session_key}_messages_{self.channel_name}")
def event_update(self, event: dict): def event_update(self, event: dict):

View File

@ -36,7 +36,8 @@ class SourceType:
class SourceTypeManager: class SourceTypeManager:
"""Manager to hold all Source types.""" """Manager to hold all Source types."""
__sources: list[SourceType] = [] def __init__(self) -> None:
self.__sources: list[SourceType] = []
def type(self): def type(self):
"""Class decorator to register classes inline.""" """Class decorator to register classes inline."""

View File

@ -74,12 +74,12 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse):
duo, self.stage.request, self.stage.get_pending_user() duo, self.stage.request, self.stage.get_pending_user()
) )
def validate(self, data: dict): def validate(self, attrs: dict):
# Checking if the given data is from a valid device class is done above # Checking if the given data is from a valid device class is done above
# Here we only check if the any data was sent at all # Here we only check if the any data was sent at all
if "code" not in data and "webauthn" not in data and "duo" not in data: if "code" not in attrs and "webauthn" not in attrs and "duo" not in attrs:
raise ValidationError("Empty response") raise ValidationError("Empty response")
return data return attrs
class AuthenticatorValidateStageView(ChallengeStageView): class AuthenticatorValidateStageView(ChallengeStageView):
@ -163,7 +163,7 @@ class AuthenticatorValidateStageView(ChallengeStageView):
# pylint: disable=unused-argument # pylint: disable=unused-argument
def challenge_valid( def challenge_valid(
self, challenge: AuthenticatorValidationChallengeResponse self, response: AuthenticatorValidationChallengeResponse
) -> HttpResponse: ) -> HttpResponse:
# All validation is done by the serializer # All validation is done by the serializer
return self.executor.stage_ok() return self.executor.stage_ok()

View File

@ -38,7 +38,7 @@ class EmailChallengeResponse(ChallengeResponse):
component = CharField(default="ak-stage-email") component = CharField(default="ak-stage-email")
def validate(self, data): def validate(self, attrs):
raise ValidationError("") raise ValidationError("")

View File

@ -73,9 +73,9 @@ class IdentificationChallengeResponse(ChallengeResponse):
pre_user: Optional[User] = None pre_user: Optional[User] = None
def validate(self, data: dict[str, Any]) -> dict[str, Any]: def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
"""Validate that user exists, and optionally their password""" """Validate that user exists, and optionally their password"""
uid_field = data["uid_field"] uid_field = attrs["uid_field"]
current_stage: IdentificationStage = self.stage.executor.current_stage current_stage: IdentificationStage = self.stage.executor.current_stage
pre_user = self.stage.get_user(uid_field) pre_user = self.stage.get_user(uid_field)
@ -101,9 +101,9 @@ class IdentificationChallengeResponse(ChallengeResponse):
self.pre_user = pre_user self.pre_user = pre_user
if not current_stage.password_stage: if not current_stage.password_stage:
# No password stage select, don't validate the password # No password stage select, don't validate the password
return data return attrs
password = data["password"] password = attrs["password"]
try: try:
user = authenticate( user = authenticate(
self.stage.request, self.stage.request,
@ -116,7 +116,7 @@ class IdentificationChallengeResponse(ChallengeResponse):
self.pre_user = user self.pre_user = user
except PermissionDenied as exc: except PermissionDenied as exc:
raise ValidationError(str(exc)) from exc raise ValidationError(str(exc)) from exc
return data return attrs
class IdentificationStageView(ChallengeStageView): class IdentificationStageView(ChallengeStageView):

View File

@ -146,8 +146,6 @@ def password_single_validator_factory() -> Callable[[PromptChallenge, str], Any]
class ListPolicyEngine(PolicyEngine): class ListPolicyEngine(PolicyEngine):
"""Slightly modified policy engine, which uses a list instead of a PolicyBindingModel""" """Slightly modified policy engine, which uses a list instead of a PolicyBindingModel"""
__list: list[Policy]
def __init__( def __init__(
self, policies: list[Policy], user: User, request: HttpRequest = None self, policies: list[Policy], user: User, request: HttpRequest = None
) -> None: ) -> None:

View File

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

View File

@ -5,8 +5,6 @@ import (
"os" "os"
) )
const VERSION = "2021.6.2"
func BUILD() string { func BUILD() string {
build := os.Getenv("GIT_BUILD_HASH") build := os.Getenv("GIT_BUILD_HASH")
if build == "" { if build == "" {
@ -18,3 +16,5 @@ func BUILD() string {
func OutpostUserAgent() string { func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD()) return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD())
} }
const VERSION = "2021.6.3"

View File

@ -98,19 +98,9 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
}, },
} }
if *u.IsActive {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"active"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "accountStatus", Values: []string{"inactive"}})
}
if u.IsSuperuser {
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"active"}})
} else {
attrs = append(attrs, &ldap.EntryAttribute{Name: "superuser", Values: []string{"inactive"}})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: pi.GroupsForUser(u)}) attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: pi.GroupsForUser(u)})
attrs = append(attrs, &ldap.EntryAttribute{Name: "goauthentik.io/ldap/active", Values: []string{BoolToString(*u.IsActive)}})
attrs = append(attrs, &ldap.EntryAttribute{Name: "goauthentik.io/ldap/superuser", Values: []string{BoolToString(u.IsSuperuser)}})
attrs = append(attrs, AKAttrsToLDAP(u.Attributes)...) attrs = append(attrs, AKAttrsToLDAP(u.Attributes)...)

View File

@ -7,6 +7,13 @@ import (
"goauthentik.io/api" "goauthentik.io/api"
) )
func BoolToString(in bool) string {
if in {
return "true"
}
return "false"
}
func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute { func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
attrList := []*ldap.EntryAttribute{} attrList := []*ldap.EntryAttribute{}
a := attrs.(*map[string]interface{}) a := attrs.(*map[string]interface{})
@ -17,6 +24,8 @@ func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
entry.Values = t entry.Values = t
case string: case string:
entry.Values = []string{t} entry.Values = []string{t}
case bool:
entry.Values = []string{BoolToString(t)}
} }
attrList = append(attrList, entry) attrList = append(attrList, entry)
} }

View File

@ -29,9 +29,10 @@ func (s *Server) bundleProviders(providers []api.ProxyOutpostConfig) []*provider
log.WithError(err).Warning("Failed to parse URL, skipping provider") log.WithError(err).Warning("Failed to parse URL, skipping provider")
} }
bundles[idx] = &providerBundle{ bundles[idx] = &providerBundle{
s: s, s: s,
Host: externalHost.Host, Host: externalHost.Host,
log: log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name), log: log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name),
endSessionUrl: provider.OidcConfiguration.EndSessionEndpoint,
} }
bundles[idx].Build(provider) bundles[idx].Build(provider)
} }

View File

@ -25,6 +25,8 @@ type providerBundle struct {
proxy *OAuthProxy proxy *OAuthProxy
Host string Host string
endSessionUrl string
cert *tls.Certificate cert *tls.Certificate
log *log.Entry log *log.Entry
@ -58,6 +60,8 @@ func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.
providerOpts.RedeemURL = provider.OidcConfiguration.TokenEndpoint providerOpts.RedeemURL = provider.OidcConfiguration.TokenEndpoint
providerOpts.OIDCJwksURL = provider.OidcConfiguration.JwksUri providerOpts.OIDCJwksURL = provider.OidcConfiguration.JwksUri
providerOpts.ProfileURL = provider.OidcConfiguration.UserinfoEndpoint providerOpts.ProfileURL = provider.OidcConfiguration.UserinfoEndpoint
providerOpts.ValidateURL = provider.OidcConfiguration.UserinfoEndpoint
providerOpts.AcrValues = "goauthentik.io/providers/oauth2/default"
if *provider.SkipPathRegex != "" { if *provider.SkipPathRegex != "" {
skipRegexes := strings.Split(*provider.SkipPathRegex, "\n") skipRegexes := strings.Split(*provider.SkipPathRegex, "\n")
@ -153,6 +157,7 @@ func (pb *providerBundle) Build(provider api.ProxyOutpostConfig) {
oauthproxy.BasicAuthPasswordAttribute = *provider.BasicAuthPasswordAttribute oauthproxy.BasicAuthPasswordAttribute = *provider.BasicAuthPasswordAttribute
} }
oauthproxy.endSessionEndpoint = pb.endSessionUrl
oauthproxy.ExternalHost = pb.Host oauthproxy.ExternalHost = pb.Host
pb.proxy = oauthproxy pb.proxy = oauthproxy

View File

@ -65,31 +65,33 @@ type OAuthProxy struct {
AuthOnlyPath string AuthOnlyPath string
UserInfoPath string UserInfoPath string
endSessionEndpoint string
mode api.ProxyMode mode api.ProxyMode
redirectURL *url.URL // the url to receive requests at
whitelistDomains []string
provider providers.Provider
sessionStore sessionsapi.SessionStore
ProxyPrefix string
serveMux http.Handler
SetXAuthRequest bool
SetBasicAuth bool
PassUserHeaders bool
BasicAuthUserAttribute string BasicAuthUserAttribute string
BasicAuthPasswordAttribute string BasicAuthPasswordAttribute string
ExternalHost string ExternalHost string
PassAccessToken bool
SetAuthorization bool redirectURL *url.URL // the url to receive requests at
PassAuthorization bool whitelistDomains []string
PreferEmailToUser bool provider providers.Provider
skipAuthRegex []string sessionStore sessionsapi.SessionStore
skipAuthPreflight bool ProxyPrefix string
skipAuthStripHeaders bool serveMux http.Handler
mainJwtBearerVerifier *oidc.IDTokenVerifier SetXAuthRequest bool
extraJwtBearerVerifiers []*oidc.IDTokenVerifier SetBasicAuth bool
compiledRegex []*regexp.Regexp PassUserHeaders bool
templates *template.Template PassAccessToken bool
realClientIPParser ipapi.RealClientIPParser SetAuthorization bool
PassAuthorization bool
PreferEmailToUser bool
skipAuthRegex []string
skipAuthPreflight bool
skipAuthStripHeaders bool
mainJwtBearerVerifier *oidc.IDTokenVerifier
extraJwtBearerVerifiers []*oidc.IDTokenVerifier
compiledRegex []*regexp.Regexp
templates *template.Template
realClientIPParser ipapi.RealClientIPParser
sessionChain alice.Chain sessionChain alice.Chain
@ -285,19 +287,13 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
// SignOut sends a response to clear the authentication cookie // SignOut sends a response to clear the authentication cookie
func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
redirect, err := p.GetRedirect(req) err := p.ClearSessionCookie(rw, req)
if err != nil {
p.logger.Errorf("Error obtaining redirect: %v", err)
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
return
}
err = p.ClearSessionCookie(rw, req)
if err != nil { if err != nil {
p.logger.Errorf("Error clearing session cookie: %v", err) p.logger.Errorf("Error clearing session cookie: %v", err)
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error()) p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
return return
} }
http.Redirect(rw, req, redirect, http.StatusFound) http.Redirect(rw, req, p.endSessionEndpoint, http.StatusFound)
} }
// AuthenticateOnly checks whether the user is currently logged in // AuthenticateOnly checks whether the user is currently logged in

View File

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

View File

@ -0,0 +1,228 @@
"""LDAP and Outpost e2e tests"""
from sys import platform
from time import sleep
from unittest.case import skipUnless
from docker.client import DockerClient, from_env
from docker.models.containers import Container
from guardian.shortcuts import get_anonymous_user
from ldap3 import (
ALL,
ALL_ATTRIBUTES,
ALL_OPERATIONAL_ATTRIBUTES,
SUBTREE,
Connection,
Server,
)
from ldap3.core.exceptions import LDAPInsufficientAccessRightsResult
from authentik.core.models import Application, Group, User
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.ldap.models import LDAPProvider
from tests.e2e.utils import (
USER,
SeleniumTestCase,
apply_migration,
object_manager,
retry,
)
@skipUnless(platform.startswith("linux"), "requires local docker")
class TestProviderLDAP(SeleniumTestCase):
"""LDAP and Outpost e2e tests"""
ldap_container: Container
def tearDown(self) -> None:
super().tearDown()
self.output_container_logs(self.ldap_container)
self.ldap_container.kill()
def start_ldap(self, outpost: Outpost) -> Container:
"""Start ldap container based on outpost created"""
client: DockerClient = from_env()
container = client.containers.run(
image="beryju.org/authentik/outpost-ldap:gh-master",
detach=True,
network_mode="host",
auto_remove=True,
environment={
"AUTHENTIK_HOST": self.live_server_url,
"AUTHENTIK_TOKEN": outpost.token.key,
},
)
return container
def _prepare(self) -> User:
"""prepare user, provider, app and container"""
# set additionalHeaders to test later
user = USER()
user.attributes["extraAttribute"] = "bar"
user.save()
ldap: LDAPProvider = LDAPProvider.objects.create(
name="ldap_provider",
authorization_flow=Flow.objects.get(slug="default-authentication-flow"),
search_group=Group.objects.first(),
)
# we need to create an application to actually access the ldap
Application.objects.create(name="ldap", slug="ldap", provider=ldap)
outpost: Outpost = Outpost.objects.create(
name="ldap_outpost",
type=OutpostType.LDAP,
)
outpost.providers.add(ldap)
outpost.save()
user = outpost.user
self.ldap_container = self.start_ldap(outpost)
# Wait until outpost healthcheck succeeds
healthcheck_retries = 0
while healthcheck_retries < 50:
if len(outpost.state) > 0:
state = outpost.state[0]
if state.last_seen:
break
healthcheck_retries += 1
sleep(0.5)
return user
@retry()
@apply_migration("authentik_core", "0003_default_user")
@apply_migration("authentik_flows", "0008_default_flows")
@object_manager
def test_ldap_bind_success(self):
"""Test simple bind"""
self._prepare()
server = Server("ldap://localhost:3389", get_info=ALL)
_connection = Connection(
server,
raise_exceptions=True,
user=f"cn={USER().username},ou=users,DC=ldap,DC=goauthentik,DC=io",
password=USER().username,
)
_connection.bind()
self.assertTrue(
Event.objects.filter(
action=EventAction.LOGIN,
user={
"pk": USER().pk,
"email": USER().email,
"username": USER().username,
},
)
)
@retry()
@apply_migration("authentik_core", "0003_default_user")
@apply_migration("authentik_flows", "0008_default_flows")
@object_manager
def test_ldap_bind_fail(self):
"""Test simple bind (failed)"""
self._prepare()
server = Server("ldap://localhost:3389", get_info=ALL)
_connection = Connection(
server,
raise_exceptions=True,
user=f"cn={USER().username},ou=users,DC=ldap,DC=goauthentik,DC=io",
password=USER().username + "fqwerwqer",
)
with self.assertRaises(LDAPInsufficientAccessRightsResult):
_connection.bind()
anon = get_anonymous_user()
self.assertTrue(
Event.objects.filter(
action=EventAction.LOGIN_FAILED,
user={"pk": anon.pk, "email": anon.email, "username": anon.username},
)
)
@retry()
@apply_migration("authentik_core", "0003_default_user")
@apply_migration("authentik_core", "0009_group_is_superuser")
@apply_migration("authentik_flows", "0008_default_flows")
@object_manager
def test_ldap_bind_search(self):
"""Test simple bind + search"""
outpost_user = self._prepare()
server = Server("ldap://localhost:3389", get_info=ALL)
_connection = Connection(
server,
raise_exceptions=True,
user=f"cn={USER().username},ou=users,dc=ldap,dc=goauthentik,dc=io",
password=USER().username,
)
_connection.bind()
self.assertTrue(
Event.objects.filter(
action=EventAction.LOGIN,
user={
"pk": USER().pk,
"email": USER().email,
"username": USER().username,
},
)
)
_connection.search(
"ou=users,dc=ldap,dc=goauthentik,dc=io",
"(objectClass=user)",
search_scope=SUBTREE,
attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES],
)
response = _connection.response
# Remove raw_attributes to make checking easier
for obj in response:
del obj["raw_attributes"]
del obj["raw_dn"]
self.assertCountEqual(
response,
[
{
"dn": f"cn={outpost_user.username},ou=users,dc=ldap,dc=goauthentik,dc=io",
"attributes": {
"cn": [outpost_user.username],
"uid": [outpost_user.uid],
"name": [""],
"displayName": [""],
"mail": [""],
"objectClass": [
"user",
"organizationalPerson",
"goauthentik.io/ldap/user",
],
"memberOf": [],
"goauthentik.io/ldap/active": ["true"],
"goauthentik.io/ldap/superuser": ["false"],
"goauthentik.io/user/override-ips": ["true"],
"goauthentik.io/user/service-account": ["true"],
},
"type": "searchResEntry",
},
{
"dn": f"cn={USER().username},ou=users,dc=ldap,dc=goauthentik,dc=io",
"attributes": {
"cn": [USER().username],
"uid": [USER().uid],
"name": [USER().name],
"displayName": [USER().name],
"mail": [USER().email],
"objectClass": [
"user",
"organizationalPerson",
"goauthentik.io/ldap/user",
],
"memberOf": [
"cn=authentik Admins,ou=groups,dc=ldap,dc=goauthentik,dc=io"
],
"goauthentik.io/ldap/active": ["true"],
"goauthentik.io/ldap/superuser": ["true"],
"extraAttribute": ["bar"],
},
"type": "searchResEntry",
},
],
)

View File

@ -119,6 +119,13 @@ class TestProviderProxy(SeleniumTestCase):
self.assertIn("X-Forwarded-Preferred-Username: akadmin", full_body_text) self.assertIn("X-Forwarded-Preferred-Username: akadmin", full_body_text)
self.assertIn("X-Foo: bar", full_body_text) self.assertIn("X-Foo: bar", full_body_text)
self.driver.get("http://localhost:4180/akprox/sign_out")
sleep(2)
full_body_text = self.driver.find_element(
By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl"
).text
self.assertIn("You've logged out of proxy.", full_body_text)
@skipUnless(platform.startswith("linux"), "requires local docker") @skipUnless(platform.startswith("linux"), "requires local docker")
class TestProviderProxyConnect(ChannelsLiveServerTestCase): class TestProviderProxyConnect(ChannelsLiveServerTestCase):

View File

@ -62,11 +62,14 @@ class OutpostDockerTests(TestCase):
) )
authentication_kp = CertificateKeyPair.objects.create( authentication_kp = CertificateKeyPair.objects.create(
name="docker-authentication", name="docker-authentication",
# pylint: disable=consider-using-with
certificate_data=open(f"{self.ssl_folder}/client/cert.pem").read(), certificate_data=open(f"{self.ssl_folder}/client/cert.pem").read(),
# pylint: disable=consider-using-with
key_data=open(f"{self.ssl_folder}/client/key.pem").read(), key_data=open(f"{self.ssl_folder}/client/key.pem").read(),
) )
verification_kp = CertificateKeyPair.objects.create( verification_kp = CertificateKeyPair.objects.create(
name="docker-verification", name="docker-verification",
# pylint: disable=consider-using-with
certificate_data=open(f"{self.ssl_folder}/client/ca.pem").read(), certificate_data=open(f"{self.ssl_folder}/client/ca.pem").read(),
) )
self.service_connection = DockerServiceConnection.objects.create( self.service_connection = DockerServiceConnection.objects.create(

View File

@ -62,11 +62,14 @@ class TestProxyDocker(TestCase):
) )
authentication_kp = CertificateKeyPair.objects.create( authentication_kp = CertificateKeyPair.objects.create(
name="docker-authentication", name="docker-authentication",
# pylint: disable=consider-using-with
certificate_data=open(f"{self.ssl_folder}/client/cert.pem").read(), certificate_data=open(f"{self.ssl_folder}/client/cert.pem").read(),
# pylint: disable=consider-using-with
key_data=open(f"{self.ssl_folder}/client/key.pem").read(), key_data=open(f"{self.ssl_folder}/client/key.pem").read(),
) )
verification_kp = CertificateKeyPair.objects.create( verification_kp = CertificateKeyPair.objects.create(
name="docker-verification", name="docker-verification",
# pylint: disable=consider-using-with
certificate_data=open(f"{self.ssl_folder}/client/ca.pem").read(), certificate_data=open(f"{self.ssl_folder}/client/ca.pem").read(),
) )
self.service_connection = DockerServiceConnection.objects.create( self.service_connection = DockerServiceConnection.objects.create(

28
web/package-lock.json generated
View File

@ -48,7 +48,7 @@
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
"moment": "^2.29.1", "moment": "^2.29.1",
"rapidoc": "^9.0.0", "rapidoc": "^9.0.0",
"rollup": "^2.52.3", "rollup": "^2.52.7",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
@ -58,7 +58,7 @@
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"typescript": "^4.3.4", "typescript": "^4.3.5",
"webcomponent-qr-code": "^1.0.5", "webcomponent-qr-code": "^1.0.5",
"yaml": "^1.10.2" "yaml": "^1.10.2"
} }
@ -6770,9 +6770,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "2.52.3", "version": "2.52.7",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz",
"integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==", "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==",
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -7604,9 +7604,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "4.3.4", "version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==", "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -13200,9 +13200,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.52.3", "version": "2.52.7",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.3.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.7.tgz",
"integrity": "sha512-QF3Sju8Kl2z0osI4unyOLyUudyhOMK6G0AeqJWgfiyigqLAlnNrfBcDWDx+f1cqn+JU2iIYVkDrgQ6/KtwEfrg==", "integrity": "sha512-55cSH4CCU6MaPr9TAOyrIC+7qFCHscL7tkNsm1MBfIJRRqRbCEY0mmeFn4Wg8FKsHtEH8r389Fz38r/o+kgXLg==",
"requires": { "requires": {
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
@ -13896,9 +13896,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "4.3.4", "version": "4.3.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz",
"integrity": "sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==" "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA=="
}, },
"uglify-js": { "uglify-js": {
"version": "3.13.0", "version": "3.13.0",

View File

@ -77,7 +77,7 @@
"lit-html": "^1.4.1", "lit-html": "^1.4.1",
"moment": "^2.29.1", "moment": "^2.29.1",
"rapidoc": "^9.0.0", "rapidoc": "^9.0.0",
"rollup": "^2.52.3", "rollup": "^2.52.7",
"rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2", "rollup-plugin-cssimport": "^1.0.2",
@ -87,7 +87,7 @@
"rollup-plugin-terser": "^7.0.2", "rollup-plugin-terser": "^7.0.2",
"ts-lit-plugin": "^1.2.1", "ts-lit-plugin": "^1.2.1",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"typescript": "^4.3.4", "typescript": "^4.3.5",
"webcomponent-qr-code": "^1.0.5", "webcomponent-qr-code": "^1.0.5",
"yaml": "^1.10.2" "yaml": "^1.10.2"
}, },

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger"; export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress"; export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current"; export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2021.6.2"; export const VERSION = "2021.6.3";
export const PAGE_SIZE = 20; export const PAGE_SIZE = 20;
export const EVENT_REFRESH = "ak-refresh"; export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle"; export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";

View File

@ -12,11 +12,11 @@ This installation method is for test-setups and small-scale productive setups.
## Preparation ## Preparation
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.6.2/docker-compose.yml). Place it in a directory of your choice. Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.6.3/docker-compose.yml). Place it in a directory of your choice.
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env` To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.6.2 >> .env` To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.6.3 >> .env`
If this is a fresh authentik install run the following commands to generate a password: If this is a fresh authentik install run the following commands to generate a password:

View File

@ -16,6 +16,8 @@ Local backups can be created by running the following command in your authentik
``` ```
docker-compose run --rm worker backup docker-compose run --rm worker backup
# Or for kubernetes
kubectl exec -it authentik-worker-.... -- ./lifecycle/bootstrap.sh backup
``` ```
This will dump the current database into the `./backups` folder. By defaults, the last 10 Backups are kept. This will dump the current database into the `./backups` folder. By defaults, the last 10 Backups are kept.
@ -26,15 +28,19 @@ Run this command in your authentik installation directory
``` ```
docker-compose run --rm worker restore docker-compose run --rm worker restore
# Or for kubernetes
kubectl exec -it authentik-worker-.... -- ./lifecycle/bootstrap.sh restore
``` ```
This will prompt you to restore from your last backup. If you want to restore from a specific file, use the `-i` flag with the filename: This will prompt you to restore from your last backup. If you want to restore from a specific file, use the `-i` flag with the filename:
``` ```
docker-compose run --rm worker restore -i default-2020-10-03-115557.psql docker-compose run --rm worker restore -i default-2020-10-03-115557.psql
# Or for kubernetes
kubectl exec -it authentik-worker-.... -- ./lifecycle/bootstrap.sh restore -i default-2020-10-03-115557.psql
``` ```
After you've restored the backup, it is recommended to restart all services with `docker-compose restart`. After you've restored the backup, it is recommended to restart all services with `docker-compose restart` or `kubectl restart deployment --all`.
### S3 Configuration ### S3 Configuration
@ -92,12 +98,15 @@ Simply enable these options in your values.yaml file
```yaml ```yaml
# Enable Database Backups to S3 # Enable Database Backups to S3
backup: authentik:
accessKey: access-key postgresql:
secretKey: secret-key s3_backup:
bucket: s3-bucket bucket: "authentik-backup"
region: eu-central-1 access_key: foo
host: s3-host secret_key: bar
region: eu-central-1
# Optional S3 host
# host: "https://backup-s3.beryju.org"
``` ```
Afterwards, run a `helm upgrade` to update the ConfigMap. Backups are done automatically as above, at 00:00 every day. Afterwards, run a `helm upgrade` to update the ConfigMap. Backups are done automatically as above, at 00:00 every day.

View File

@ -40,9 +40,9 @@ The following fields are currently sent for users:
- "user" - "user"
- "organizationalPerson" - "organizationalPerson"
- "goauthentik.io/ldap/user" - "goauthentik.io/ldap/user"
- `accountStatus`: "active" if the account is active, otherwise "inactive"
- `superuser`: "active" if the account is part of a group with superuser permissions, otherwise "inactive"
- `memberOf`: A list of all DNs that the user is a member of - `memberOf`: A list of all DNs that the user is a member of
- `goauthentik.io/ldap/active`: "true" if the account is active, otherwise "false"
- `goauthentik.io/ldap/superuser`: "true" if the account is part of a group with superuser permissions, otherwise "false"
The following fields are current set for groups: The following fields are current set for groups:

View File

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

View File

@ -14,7 +14,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
name: authentik-outpost-api name: authentik-outpost-api
stringData: stringData:
authentik_host: "__AUTHENTIK_URL__" authentik_host: "__AUTHENTIK_URL__"
@ -29,7 +29,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
name: authentik-outpost name: authentik-outpost
spec: spec:
ports: ports:
@ -54,7 +54,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
name: authentik-outpost name: authentik-outpost
spec: spec:
selector: selector:
@ -62,14 +62,14 @@ spec:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
template: template:
metadata: metadata:
labels: labels:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
spec: spec:
containers: containers:
- env: - env:
@ -88,7 +88,7 @@ spec:
secretKeyRef: secretKeyRef:
key: authentik_host_insecure key: authentik_host_insecure
name: authentik-outpost-api name: authentik-outpost-api
image: ghcr.io/goauthentik/proxy:2021.6.2 image: ghcr.io/goauthentik/proxy:2021.6.3
name: proxy name: proxy
ports: ports:
- containerPort: 4180 - containerPort: 4180
@ -110,7 +110,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.6.2 app.kubernetes.io/version: 2021.6.3
name: authentik-outpost name: authentik-outpost
spec: spec:
rules: rules:

View File

@ -46,24 +46,46 @@ import TabItem from '@theme/TabItem';
<TabItem value="standalone-nginx"> <TabItem value="standalone-nginx">
``` ```
location /akprox { server {
proxy_pass http://*ip of your outpost*:4180; # SSL and VHost configuration
error_page 401 = @akprox_signin; listen 443 ssl http2;
proxy_set_header X-Forwarded-Host $http_host; server_name _;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
}
location @akprox_signin { ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
internal; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
add_header Set-Cookie $auth_cookie;
return 302 /akprox/start?rd=$request_uri;
}
location / { location / {
auth_request /akprox/auth?nginx; # Put your proxy_pass to your application here
# All your other options... # proxy_pass http://localhost:5000;
# authentik-specific config
auth_request /akprox/auth;
error_page 401 = @akprox_signin;
# translate headers from the outposts back to the actual upstream
auth_request_set $username $upstream_http_x_auth_username;
auth_request_set $email $upstream_http_X_Forwarded_Email;
proxy_set_header X-Auth-Username $username;
proxy_set_header X-Forwarded-Email $email;
} }
# all requests to /akprox must be accessible without authentication
location /akprox {
proxy_pass http://*ip or hostname of the authentik OUTPOST*:4180;
# ensure the host of this vserver matches your external URL you've configured
# in authentik
proxy_set_header Host $host;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_cookie $upstream_http_set_cookie;
}
# Special location for when the /auth endpoint returns a 401,
# redirect to the /start URL which initiates SSO
location @akprox_signin {
internal;
add_header Set-Cookie $auth_cookie;
return 302 /akprox/start?rd=$request_uri;
}
}
``` ```
</TabItem> </TabItem>