Compare commits
41 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b7ebf0e07 | |||
| ddca8ef3ca | |||
| 709581f5a8 | |||
| 72e41c03f5 | |||
| 40503d06b7 | |||
| 1df8790050 | |||
| 3c23ad340f | |||
| f9f2e00913 | |||
| 8362507bdf | |||
| a2181c3bf0 | |||
| a07ded0dae | |||
| 3b0b9301ee | |||
| 919f293fc7 | |||
| c4df2e5a50 | |||
| 4d1500e0f3 | |||
| 281bd4c69a | |||
| e4678aa032 | |||
| ff1c4d555a | |||
| 4a3e34d40a | |||
| 6939898bbe | |||
| 549607c5ed | |||
| f61acdfbfd | |||
| e3572bad76 | |||
| 8f99891a9d | |||
| 99d5262d41 | |||
| 97a3c2d88b | |||
| e91ff4566d | |||
| dc942b2f4c | |||
| a3fccbdaff | |||
| bdf9f26d07 | |||
| 901cea1453 | |||
| 37b57ac28f | |||
| e9aa37ba67 | |||
| 9a0aa4c79b | |||
| 34ab68a169 | |||
| 52cf4890cf | |||
| 8e5d03cb86 | |||
| 2190fa555b | |||
| ae1edde17b | |||
| 3ad1c3f212 | |||
| 65ec444e52 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2021.5.1-rc8
|
||||
current_version = 2021.5.1
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@ -36,9 +36,9 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik:2021.5.1-rc8,
|
||||
beryju/authentik:2021.5.1,
|
||||
beryju/authentik:latest,
|
||||
ghcr.io/goauthentik/server:2021.5.1-rc8,
|
||||
ghcr.io/goauthentik/server:2021.5.1,
|
||||
ghcr.io/goauthentik/server:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
@ -75,9 +75,9 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-proxy:2021.5.1-rc8,
|
||||
beryju/authentik-proxy:2021.5.1,
|
||||
beryju/authentik-proxy:latest,
|
||||
ghcr.io/goauthentik/proxy:2021.5.1-rc8,
|
||||
ghcr.io/goauthentik/proxy:2021.5.1,
|
||||
ghcr.io/goauthentik/proxy:latest
|
||||
context: outpost/
|
||||
file: outpost/proxy.Dockerfile
|
||||
@ -115,9 +115,9 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-ldap:2021.5.1-rc8,
|
||||
beryju/authentik-ldap:2021.5.1,
|
||||
beryju/authentik-ldap:latest,
|
||||
ghcr.io/goauthentik/ldap:2021.5.1-rc8,
|
||||
ghcr.io/goauthentik/ldap:2021.5.1,
|
||||
ghcr.io/goauthentik/ldap:latest
|
||||
context: outpost/
|
||||
file: outpost/ldap.Dockerfile
|
||||
@ -139,7 +139,7 @@ jobs:
|
||||
docker-compose pull -q
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "apt-get update && apt-get install -y --no-install-recommends git && pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
sentry-release:
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
needs:
|
||||
@ -155,5 +155,5 @@ jobs:
|
||||
SENTRY_PROJECT: authentik
|
||||
SENTRY_URL: https://sentry.beryju.org
|
||||
with:
|
||||
version: authentik@2021.5.1-rc8
|
||||
version: authentik@2021.5.1
|
||||
environment: beryjuorg-prod
|
||||
|
||||
6
.github/workflows/tag.yml
vendored
6
.github/workflows/tag.yml
vendored
@ -27,17 +27,17 @@ jobs:
|
||||
-f Dockerfile .
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
docker-compose run -u root --entrypoint /bin/bash server -c "apt-get update && apt-get install -y --no-install-recommends git && pip install --no-cache -r requirements-dev.txt && ./manage.py test authentik"
|
||||
- name: Extract version number
|
||||
id: get_version
|
||||
uses: actions/github-script@0.2.0
|
||||
uses: actions/github-script@v4.0.2
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
return context.payload.ref.replace(/\/refs\/tags\/version\//, '');
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.0.0
|
||||
uses: actions/create-release@v1.1.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@ -48,7 +48,7 @@ ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends curl ca-certificates gnupg git && \
|
||||
apt-get install -y --no-install-recommends curl ca-certificates gnupg git runit && \
|
||||
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
|
||||
apt-get update && \
|
||||
@ -58,14 +58,7 @@ RUN apt-get update && \
|
||||
apt-get autoremove --purge -y && \
|
||||
apt-get clean && \
|
||||
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
|
||||
# This is quite hacky, but docker has no guaranteed Group ID
|
||||
# we could instead check for the GID of the socket and add the user dynamically,
|
||||
# but then we have to drop permmissions later
|
||||
groupadd -g 998 docker_998 && \
|
||||
groupadd -g 999 docker_999 && \
|
||||
adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \
|
||||
usermod -a -G docker_998 authentik && \
|
||||
usermod -a -G docker_999 authentik && \
|
||||
mkdir /backups && \
|
||||
chown authentik:authentik /backups
|
||||
|
||||
@ -77,7 +70,6 @@ COPY ./lifecycle/ /lifecycle
|
||||
COPY --from=builder /work/authentik /authentik-proxy
|
||||
|
||||
USER authentik
|
||||
STOPSIGNAL SIGINT
|
||||
ENV TMPDIR /dev/shm/
|
||||
ENV PYTHONUBUFFERED 1
|
||||
ENTRYPOINT [ "/lifecycle/bootstrap.sh" ]
|
||||
|
||||
6
Makefile
6
Makefile
@ -36,11 +36,5 @@ gen:
|
||||
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=authentik-api,npmVersion=1.0.0
|
||||
cd web/api && npx tsc
|
||||
|
||||
local-stack:
|
||||
export AUTHENTIK_TAG=testing
|
||||
docker build -t beryju/authentik:testng .
|
||||
docker-compose up -d
|
||||
docker-compose run --rm server migrate
|
||||
|
||||
run:
|
||||
go run -v cmd/server/main.go
|
||||
|
||||
75
Pipfile.lock
generated
75
Pipfile.lock
generated
@ -122,19 +122,18 @@
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:bcb1b76ca5a60586181ad202b19f4c50cb7c22ac68cae20f997abe311e22bf2a",
|
||||
"sha256:edf9b3b36e08cd575a9458bf59871852335aceb5db2d07bfc8530bae3a97d045"
|
||||
"sha256:3317722a1e9acbfc0d30cdf273d1708c823ceb19309e9cd91cac8a3604762341",
|
||||
"sha256:ee3317fd79b443ef102469fac393a1ffb650ea51ac4fc27464013872c5e1ce31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.17.71"
|
||||
"version": "==1.17.72"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:414e1721d381095767db1cf673257fdfec639da3be9405a41d49cc859b817d68",
|
||||
"sha256:b7afebca1fd6ca1f8af79f377a445d474e3bd2cf88e704169d6713a6362a304f"
|
||||
"sha256:0fa93a2e2daad5791c63ee526ada66896cc483d04cb2d32bfcadfeb881203453"
|
||||
],
|
||||
"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.71"
|
||||
"version": "==1.20.72"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
@ -323,11 +322,11 @@
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:0a1d195ad65c52bf275b8277b3d49680bd1137a5f55039a806f25f6b9752ce3d",
|
||||
"sha256:18dd3145ddbd04bf189ff79b9954d08fda5171ea7b57bf705789fea766a07d50"
|
||||
"sha256:13ac78dbfd189532cad8f383a27e58e18b3d33f80009ceb476d7fcbfc5dcebd8",
|
||||
"sha256:7e0a1393d18c16b503663752a8b6790880c5084412618990ce8a81cc908b4962"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.2"
|
||||
"version": "==3.2.3"
|
||||
},
|
||||
"django-dbbackup": {
|
||||
"git": "https://github.com/django-dbbackup/django-dbbackup.git",
|
||||
@ -441,11 +440,11 @@
|
||||
},
|
||||
"geoip2": {
|
||||
"hashes": [
|
||||
"sha256:57d8d15de2527e0697bbef44fc16812bba709f03a07ef99297bd56c1df3b1efd",
|
||||
"sha256:707025542ef076bd8fd80e97138bebdb7812527b2a007d141a27ad98b0370fff"
|
||||
"sha256:906a1dbf15a179a1af3522970e8420ab15bb3e0afc526942cc179e12146d9c1d",
|
||||
"sha256:b97b44031fdc463e84eb1316b4f19edd978cb1d78703465fcb1e36dc5a822ba6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1.0"
|
||||
"version": "==4.2.0"
|
||||
},
|
||||
"google-auth": {
|
||||
"hashes": [
|
||||
@ -607,19 +606,19 @@
|
||||
},
|
||||
"kubernetes": {
|
||||
"hashes": [
|
||||
"sha256:23c85d8571df8f56e773f1a413bc081537536dc47e2b5e8dc2e6262edb2c57ca",
|
||||
"sha256:ec52ea01d52e2ec3da255992f7e859f3a76f2bdb51cf65ba8cd71dfc309d8daa"
|
||||
"sha256:225a95a0aadbd5b645ab389d941a7980db8cdad2a776fde64d1b43fc3299bde9",
|
||||
"sha256:c69b318696ba797dcf63eb928a8d4370c52319f4140023c502d7dfdf2080eb79"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==12.0.1"
|
||||
"version": "==17.17.0"
|
||||
},
|
||||
"ldap3": {
|
||||
"hashes": [
|
||||
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57",
|
||||
"sha256:18c3ee656a6775b9b0d60f7c6c5b094d878d1d90fc03d56731039f0a4b546a91",
|
||||
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59",
|
||||
"sha256:8c949edbad2be8a03e719ba48bd6779f327ec156929562814b3e84ab56889c8c",
|
||||
"sha256:afc6fc0d01f02af82cd7bfabd3bbfd5dc96a6ae91e97db0a2dab8a0f1b436056",
|
||||
"sha256:c1df41d89459be6f304e0ceec4b00fdea533dbbcd83c802b1272dcdb94620b57"
|
||||
"sha256:4139c91f0eef9782df7b77c8cbc6243086affcb6a8a249b768a9658438e5da59"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.9"
|
||||
@ -874,37 +873,37 @@
|
||||
},
|
||||
"pyasn1": {
|
||||
"hashes": [
|
||||
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
|
||||
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
|
||||
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
|
||||
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
|
||||
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
|
||||
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
|
||||
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
|
||||
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
|
||||
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
|
||||
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
|
||||
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
|
||||
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
|
||||
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3",
|
||||
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
|
||||
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
|
||||
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
|
||||
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
|
||||
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
|
||||
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
|
||||
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
|
||||
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"
|
||||
],
|
||||
"version": "==0.4.8"
|
||||
},
|
||||
"pyasn1-modules": {
|
||||
"hashes": [
|
||||
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
|
||||
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
|
||||
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
|
||||
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
|
||||
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
|
||||
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
|
||||
"sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
|
||||
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
|
||||
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
|
||||
"sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
|
||||
"sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
|
||||
"sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
|
||||
"sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
|
||||
"sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
|
||||
"sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
|
||||
"sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
|
||||
"sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
|
||||
"sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
|
||||
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
|
||||
"sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405",
|
||||
"sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
|
||||
"sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"
|
||||
],
|
||||
"version": "==0.2.8"
|
||||
},
|
||||
@ -1603,11 +1602,11 @@
|
||||
},
|
||||
"gitpython": {
|
||||
"hashes": [
|
||||
"sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b",
|
||||
"sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"
|
||||
"sha256:2bfcd25e6b81fe431fa3ab1f0975986cfddabf7870a323c183f3afbc9447c0c5",
|
||||
"sha256:37ac36cacf2e2be5e88f0810187c5833e71c1a2a8cf81588f5699d1b70183baa"
|
||||
],
|
||||
"markers": "python_version >= '3.4'",
|
||||
"version": "==3.1.14"
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.1.16"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
"""authentik"""
|
||||
__version__ = "2021.5.1-rc8"
|
||||
__version__ = "2021.5.1"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
@ -75,5 +75,6 @@ def backup_database(self: MonitoredTask): # pragma: no cover
|
||||
Boto3Error,
|
||||
PermissionError,
|
||||
CommandConnectorError,
|
||||
ValueError,
|
||||
) as exc:
|
||||
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
|
||||
|
||||
@ -2,22 +2,25 @@
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.groups import GroupSerializer
|
||||
from authentik.events.models import NotificationRule
|
||||
|
||||
|
||||
class NotificationRuleSerializer(ModelSerializer):
|
||||
"""NotificationRule Serializer"""
|
||||
|
||||
group_obj = GroupSerializer(read_only=True, source="group")
|
||||
|
||||
class Meta:
|
||||
|
||||
model = NotificationRule
|
||||
depth = 2
|
||||
fields = [
|
||||
"pk",
|
||||
"name",
|
||||
"transports",
|
||||
"severity",
|
||||
"group",
|
||||
"group_obj",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -210,6 +210,7 @@ class FlowViewSet(ModelViewSet):
|
||||
request.user, "authentik_policies.view_policybinding"
|
||||
)
|
||||
.filter(target=stage_binding)
|
||||
.exclude(policy__isnull=True)
|
||||
.order_by("order")
|
||||
):
|
||||
body.append(
|
||||
|
||||
@ -42,7 +42,8 @@ outposts:
|
||||
# Placeholders:
|
||||
# %(type)s: Outpost type; proxy, ldap, etc
|
||||
# %(version)s: Current version; 2021.4.1
|
||||
docker_image_base: "beryju/authentik-%(type)s:%(version)s"
|
||||
# %(build_hash)s: Build hash if you're running a beta version
|
||||
docker_image_base: "ghcr.io/goauthentik/%(type)s:%(version)s"
|
||||
|
||||
authentik:
|
||||
avatars: gravatar # gravatar or none
|
||||
|
||||
@ -4,6 +4,7 @@ from typing import Optional
|
||||
from aioredis.errors import ConnectionClosedError, ReplyError
|
||||
from billiard.exceptions import WorkerLostError
|
||||
from botocore.client import ClientError
|
||||
from botocore.exceptions import BotoCoreError
|
||||
from celery.exceptions import CeleryError
|
||||
from channels.middleware import BaseMiddleware
|
||||
from channels_redis.core import ChannelFull
|
||||
@ -72,6 +73,7 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
|
||||
WorkerLostError,
|
||||
CeleryError,
|
||||
# S3 errors
|
||||
BotoCoreError,
|
||||
ClientError,
|
||||
# custom baseclass
|
||||
SentryIgnoredException,
|
||||
@ -87,6 +89,6 @@ def before_send(event: dict, hint: dict) -> Optional[dict]:
|
||||
if isinstance(exc_value, ignored_classes):
|
||||
return None
|
||||
if "logger" in event:
|
||||
if event["logger"] in ["dbbackup"]:
|
||||
if event["logger"] in ["dbbackup", "botocore"]:
|
||||
return None
|
||||
return event
|
||||
|
||||
@ -18,8 +18,6 @@ class OutpostSerializer(ModelSerializer):
|
||||
"""Outpost Serializer"""
|
||||
|
||||
config = JSONField(validators=[is_dict], source="_config")
|
||||
# TODO: Remove _config again, this is only here for legacy with older outposts
|
||||
_config = JSONField(validators=[is_dict], read_only=True)
|
||||
providers_obj = ProviderSerializer(source="providers", many=True, read_only=True)
|
||||
|
||||
def validate_config(self, config) -> dict:
|
||||
@ -42,7 +40,6 @@ class OutpostSerializer(ModelSerializer):
|
||||
"service_connection",
|
||||
"token_identifier",
|
||||
"config",
|
||||
"_config",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ class OutpostConsumer(AuthJsonConsumer):
|
||||
)
|
||||
if msg.instruction == WebsocketMessageInstruction.HELLO:
|
||||
state.version = msg.args.get("version", None)
|
||||
state.build_hash = msg.args.get("buildHash", "")
|
||||
elif msg.instruction == WebsocketMessageInstruction.ACK:
|
||||
return
|
||||
state.save(timeout=OUTPOST_HELLO_INTERVAL * 1.5)
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
"""Base Controller"""
|
||||
from dataclasses import dataclass
|
||||
from os import environ
|
||||
from typing import Optional
|
||||
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.testing import capture_logs
|
||||
|
||||
from authentik import __version__
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
from authentik.outposts.models import Outpost, OutpostServiceConnection
|
||||
@ -69,4 +70,8 @@ class BaseController:
|
||||
def get_container_image(self) -> str:
|
||||
"""Get container image to use for this outpost"""
|
||||
image_name_template: str = CONFIG.y("outposts.docker_image_base")
|
||||
return image_name_template % {"type": self.outpost.type, "version": __version__}
|
||||
return image_name_template % {
|
||||
"type": self.outpost.type,
|
||||
"version": __version__,
|
||||
"build_hash": environ.get(ENV_GIT_HASH_KEY, ""),
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"""Outpost models"""
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from datetime import datetime
|
||||
from os import environ
|
||||
from typing import Iterable, Optional, Union
|
||||
from uuid import uuid4
|
||||
|
||||
@ -26,7 +27,7 @@ from packaging.version import LegacyVersion, Version, parse
|
||||
from structlog.stdlib import get_logger
|
||||
from urllib3.exceptions import HTTPError
|
||||
|
||||
from authentik import __version__
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
from authentik.core.models import USER_ATTRIBUTE_SA, Provider, Token, TokenIntents, User
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.lib.config import CONFIG
|
||||
@ -411,6 +412,7 @@ class OutpostState:
|
||||
last_seen: Optional[datetime] = field(default=None)
|
||||
version: Optional[str] = field(default=None)
|
||||
version_should: Union[Version, LegacyVersion] = field(default=OUR_VERSION)
|
||||
build_hash: str = field(default="")
|
||||
|
||||
_outpost: Optional[Outpost] = field(default=None)
|
||||
|
||||
@ -419,6 +421,8 @@ class OutpostState:
|
||||
"""Check if outpost version matches our version"""
|
||||
if not self.version:
|
||||
return False
|
||||
if self.build_hash != environ.get(ENV_GIT_HASH_KEY, ""):
|
||||
return False
|
||||
return parse(self.version) < OUR_VERSION
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -320,6 +320,7 @@ CELERY_RESULT_BACKEND = (
|
||||
# Database backup
|
||||
DBBACKUP_STORAGE = "django.core.files.storage.FileSystemStorage"
|
||||
DBBACKUP_STORAGE_OPTIONS = {"location": "./backups" if DEBUG else "/backups"}
|
||||
DBBACKUP_FILENAME_TEMPLATE = "authentik-backup-{datetime}.sql"
|
||||
if CONFIG.y("postgresql.s3_backup"):
|
||||
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
DBBACKUP_STORAGE_OPTIONS = {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
"""AuthenticatorStaticStage API Views"""
|
||||
from django_filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django_otp.plugins.otp_static.models import StaticDevice
|
||||
from guardian.utils import get_anonymous_user
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
||||
|
||||
@ -21,7 +21,7 @@ services:
|
||||
networks:
|
||||
- internal
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc8}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.5.1}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -52,7 +52,7 @@ services:
|
||||
- "0.0.0.0:9000:9000"
|
||||
- "0.0.0.0:9443:9443"
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-beryju/authentik}:${AUTHENTIK_TAG:-2021.5.1-rc8}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.5.1}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
networks:
|
||||
@ -64,8 +64,13 @@ services:
|
||||
AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
|
||||
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
|
||||
# AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
|
||||
# This is optional, and can be removed. If you remove this, the following will happen
|
||||
# - The permissions for the /backups and /media folders aren't fixed, so make sure they are 1000:1000
|
||||
# - The docker socket can't be accessed anymore
|
||||
user: root
|
||||
volumes:
|
||||
- ./backups:/backups
|
||||
- ./media:/media
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./custom-templates:/templates
|
||||
- geoip:/geoip
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
package constants
|
||||
|
||||
const VERSION = "2021.5.1-rc8"
|
||||
const VERSION = "2021.5.1"
|
||||
|
||||
@ -1,14 +1,29 @@
|
||||
#!/bin/bash -e
|
||||
python -m lifecycle.wait_for_db
|
||||
printf '{"event": "Bootstrap completed", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
|
||||
|
||||
function check_if_root {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
printf '{"event": "Not running as root, disabling permission fixes", "level": "info", "logger": "bootstrap", "command": "%s"}\n' "$@" > /dev/stderr
|
||||
return
|
||||
fi
|
||||
SOCKET="/var/run/docker.sock"
|
||||
if [[ -e "$SOCKET" ]]; then
|
||||
# Get group ID of the docker socket, so we can create a matching group and
|
||||
# add ourselves to it
|
||||
DOCKER_GID=$(stat -c '%g' $SOCKET)
|
||||
usermod -a -G $DOCKER_GID authentik
|
||||
fi
|
||||
# Fix permissions of backups and media
|
||||
chown -R authentik:authentik /media /backups
|
||||
}
|
||||
|
||||
if [[ "$1" == "server" ]]; then
|
||||
python -m lifecycle.migrate
|
||||
/authentik-proxy
|
||||
elif [[ "$1" == "worker" ]]; then
|
||||
celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events
|
||||
elif [[ "$1" == "migrate" ]]; then
|
||||
printf "DEPERECATED: database migrations are now executed automatically on startup."
|
||||
python -m lifecycle.migrate
|
||||
check_if_root
|
||||
chpst -u authentik env HOME=/authentik celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events
|
||||
elif [[ "$1" == "backup" ]]; then
|
||||
python -m manage dbbackup --clean
|
||||
elif [[ "$1" == "restore" ]]; then
|
||||
|
||||
@ -113,10 +113,21 @@ stages:
|
||||
inputs:
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/outpost-proxy'
|
||||
command: 'buildAndPush'
|
||||
command: 'build'
|
||||
Dockerfile: 'outpost/proxy.Dockerfile'
|
||||
buildContext: 'outpost/'
|
||||
tags: "gh-$(branchName)"
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
|
||||
- task: Docker@2
|
||||
inputs:
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/outpost-proxy'
|
||||
command: 'push'
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
- job: ldap_build_docker
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
@ -138,7 +149,18 @@ stages:
|
||||
inputs:
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/outpost-ldap'
|
||||
command: 'buildAndPush'
|
||||
command: 'build'
|
||||
Dockerfile: 'outpost/ldap.Dockerfile'
|
||||
buildContext: 'outpost/'
|
||||
tags: "gh-$(branchName)"
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
arguments: '--build-arg GIT_BUILD_HASH=$(Build.SourceVersion)'
|
||||
- task: Docker@2
|
||||
inputs:
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/outpost-ldap'
|
||||
command: 'push'
|
||||
tags: |
|
||||
gh-$(branchName)
|
||||
gh-$(Build.SourceVersion)
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
FROM golang:1.16.4 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -43,7 +42,7 @@ type APIController struct {
|
||||
// NewAPIController initialise new API Controller instance from URL and API token
|
||||
func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
transport := httptransport.New(akURL.Host, client.DefaultBasePath, []string{akURL.Scheme})
|
||||
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
|
||||
transport.Transport = SetUserAgent(getTLSTransport(), pkg.UserAgent())
|
||||
|
||||
// create the transport
|
||||
auth := httptransport.BearerToken(token)
|
||||
|
||||
@ -23,7 +23,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
||||
|
||||
header := http.Header{
|
||||
"Authorization": []string{authHeader},
|
||||
"User-Agent": []string{fmt.Sprintf("authentik-proxy@%s", pkg.VERSION)},
|
||||
"User-Agent": []string{pkg.UserAgent()},
|
||||
}
|
||||
|
||||
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
|
||||
@ -47,6 +47,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
||||
Instruction: WebsocketInstructionHello,
|
||||
Args: map[string]interface{}{
|
||||
"version": pkg.VERSION,
|
||||
"buildHash": pkg.BUILD(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
@ -76,7 +77,7 @@ func (ac *APIController) startWSHandler() {
|
||||
var wsMsg websocketMessage
|
||||
err := ac.wsConn.ReadJSON(&wsMsg)
|
||||
if err != nil {
|
||||
logger.Println("read:", err)
|
||||
logger.WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
continue
|
||||
}
|
||||
@ -101,13 +102,14 @@ func (ac *APIController) startWSHealth() {
|
||||
Instruction: WebsocketInstructionHello,
|
||||
Args: map[string]interface{}{
|
||||
"version": pkg.VERSION,
|
||||
"buildHash": pkg.BUILD(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
err := ac.wsConn.WriteJSON(aliveMsg)
|
||||
ac.logger.WithField("loop", "ws-health").Trace("hello'd")
|
||||
if err != nil {
|
||||
ac.logger.WithField("loop", "ws-health").Println("write:", err)
|
||||
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
continue
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ func doGlobalSetup(config map[string]interface{}) {
|
||||
default:
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.WithField("version", pkg.VERSION).Info("Starting authentik outpost")
|
||||
log.WithField("buildHash", pkg.BUILD()).WithField("version", pkg.VERSION).Info("Starting authentik outpost")
|
||||
|
||||
var dsn string
|
||||
if config[ConfigErrorReportingEnabled].(bool) {
|
||||
|
||||
@ -2,20 +2,22 @@ package ldap
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
ls.log.WithField("boundDN", bindDN).Info("bind")
|
||||
ls.log.WithField("bindDN", bindDN).Info("bind")
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
for _, instance := range ls.providers {
|
||||
username, err := instance.getUsername(bindDN)
|
||||
if err == nil {
|
||||
return instance.Bind(username, bindPW, conn)
|
||||
return instance.Bind(username, bindDN, bindPW, conn)
|
||||
} else {
|
||||
ls.log.WithError(err).Debug("Username not for instance")
|
||||
}
|
||||
}
|
||||
ls.log.WithField("boundDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
|
||||
ls.log.WithField("bindDN", bindDN).WithField("request", "bind").Warning("No provider found for request")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ func (pi *ProviderInstance) getUsername(dn string) (string, error) {
|
||||
return "", errors.New("failed to find cn")
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
pi.log.WithError(err).Warning("Failed to create cookiejar")
|
||||
@ -67,9 +67,9 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode())
|
||||
passed, err := pi.solveFlowChallenge(username, bindPW, client, params.Encode(), 1)
|
||||
if err != nil {
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to solve challenge")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to solve challenge")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
if !passed {
|
||||
@ -82,25 +82,25 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
||||
}, httptransport.PassThroughAuth)
|
||||
if err != nil {
|
||||
if _, denied := err.(*core.CoreApplicationsCheckAccessForbidden); denied {
|
||||
pi.log.WithField("boundDN", username).Info("Access denied for user")
|
||||
pi.log.WithField("bindDN", bindDN).Info("Access denied for user")
|
||||
return ldap.LDAPResultInsufficientAccessRights, nil
|
||||
}
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to check access")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to check access")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
pi.log.WithField("boundDN", username).Info("User has access")
|
||||
pi.log.WithField("bindDN", bindDN).Info("User has access")
|
||||
// Get user info to store in context
|
||||
userInfo, err := pi.s.ac.Client.Core.CoreUsersMe(&core.CoreUsersMeParams{
|
||||
Context: context.Background(),
|
||||
HTTPClient: client,
|
||||
}, httptransport.PassThroughAuth)
|
||||
if err != nil {
|
||||
pi.log.WithField("boundDN", username).WithError(err).Warning("failed to get user info")
|
||||
pi.log.WithField("bindDN", bindDN).WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
pi.boundUsersMutex.Lock()
|
||||
pi.boundUsers[username] = UserFlags{
|
||||
UserInfo: userInfo.Payload.User,
|
||||
pi.boundUsers[bindDN] = UserFlags{
|
||||
UserInfo: *userInfo.Payload.User,
|
||||
CanSearch: pi.SearchAccessCheck(userInfo.Payload.User),
|
||||
}
|
||||
defer pi.boundUsersMutex.Unlock()
|
||||
@ -112,7 +112,8 @@ func (pi *ProviderInstance) Bind(username string, bindPW string, conn net.Conn)
|
||||
func (pi *ProviderInstance) SearchAccessCheck(user *models.User) bool {
|
||||
for _, group := range user.Groups {
|
||||
for _, allowedGroup := range pi.searchAllowedGroups {
|
||||
if &group.Pk == allowedGroup {
|
||||
pi.log.WithField("userGroup", group.Pk).WithField("allowedGroup", allowedGroup).Trace("Checking search access")
|
||||
if group.Pk.String() == allowedGroup.String() {
|
||||
pi.log.WithField("group", group.Name).Info("Allowed access to search")
|
||||
return true
|
||||
}
|
||||
@ -139,7 +140,7 @@ func (pi *ProviderInstance) delayDeleteUserInfo(dn string) {
|
||||
}()
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string) (bool, error) {
|
||||
func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, client *http.Client, urlParams string, depth int) (bool, error) {
|
||||
challenge, err := pi.s.ac.Client.Flows.FlowsExecutorGet(&flows.FlowsExecutorGetParams{
|
||||
FlowSlug: pi.flowSlug,
|
||||
Query: urlParams,
|
||||
@ -169,6 +170,10 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
|
||||
}
|
||||
response, err := pi.s.ac.Client.Flows.FlowsExecutorSolve(responseParams, pi.s.ac.Auth)
|
||||
pi.log.WithField("component", response.Payload.Component).WithField("type", *response.Payload.Type).Debug("Got response")
|
||||
switch response.Payload.Component {
|
||||
case "ak-stage-access-denied":
|
||||
return false, errors.New("got ak-stage-access-denied")
|
||||
}
|
||||
if *response.Payload.Type == "redirect" {
|
||||
return true, nil
|
||||
}
|
||||
@ -184,5 +189,8 @@ func (pi *ProviderInstance) solveFlowChallenge(bindDN string, password string, c
|
||||
}
|
||||
}
|
||||
}
|
||||
return pi.solveFlowChallenge(bindDN, password, client, urlParams)
|
||||
if depth >= 10 {
|
||||
return false, errors.New("exceeded stage recursion depth")
|
||||
}
|
||||
return pi.solveFlowChallenge(bindDN, password, client, urlParams, depth+1)
|
||||
}
|
||||
|
||||
@ -29,10 +29,13 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest,
|
||||
pi.boundUsersMutex.RLock()
|
||||
defer pi.boundUsersMutex.RUnlock()
|
||||
flags, ok := pi.boundUsers[bindDN]
|
||||
pi.log.WithField("bindDN", bindDN).WithField("ok", ok).Debugf("%+v\n", flags)
|
||||
if !ok {
|
||||
pi.log.Debug("User info not cached")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
if !flags.CanSearch {
|
||||
pi.log.Debug("User can't search")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ type ProviderInstance struct {
|
||||
}
|
||||
|
||||
type UserFlags struct {
|
||||
UserInfo *models.User
|
||||
UserInfo models.User
|
||||
CanSearch bool
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,8 @@ import (
|
||||
"github.com/nmcclain/ldap"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
ls.log.WithField("boundDN", boundDN).WithField("baseDN", searchReq.BaseDN).Info("search")
|
||||
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
ls.log.WithField("bindDN", bindDN).WithField("baseDN", searchReq.BaseDN).Info("search")
|
||||
if searchReq.BaseDN == "" {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
@ -21,7 +21,7 @@ func (ls *LDAPServer) Search(boundDN string, searchReq ldap.SearchRequest, conn
|
||||
for _, provider := range ls.providers {
|
||||
providerBase, _ := goldap.ParseDN(provider.BaseDN)
|
||||
if providerBase.AncestorOf(bd) {
|
||||
return provider.Search(boundDN, searchReq, conn)
|
||||
return provider.Search(bindDN, searchReq, conn)
|
||||
}
|
||||
}
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("no provider could handle request")
|
||||
|
||||
@ -161,7 +161,7 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
|
||||
p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
|
||||
return
|
||||
}
|
||||
redirectURI := p.GetRedirectURI(getHost(req))
|
||||
redirectURI := p.GetRedirectURI(req.Host)
|
||||
http.Redirect(rw, req, p.provider.GetLoginURL(redirectURI, fmt.Sprintf("%v:%v", nonce, redirect)), http.StatusFound)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,16 @@
|
||||
package pkg
|
||||
|
||||
const VERSION = "2021.5.1-rc8"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const VERSION = "2021.5.1"
|
||||
|
||||
func BUILD() string {
|
||||
return os.Getenv("GIT_BUILD_HASH")
|
||||
}
|
||||
|
||||
func UserAgent() string {
|
||||
return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD())
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
FROM golang:1.16.4 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
||||
89
swagger.yaml
89
swagger.yaml
@ -15694,6 +15694,7 @@ definitions:
|
||||
NotificationRule:
|
||||
required:
|
||||
- name
|
||||
- transports
|
||||
type: object
|
||||
properties:
|
||||
pk:
|
||||
@ -15706,38 +15707,17 @@ definitions:
|
||||
type: string
|
||||
minLength: 1
|
||||
transports:
|
||||
description: Select which transports should be used to notify the user. If
|
||||
none are selected, the notification will only be shown in the authentik
|
||||
UI.
|
||||
type: array
|
||||
items:
|
||||
required:
|
||||
- name
|
||||
- mode
|
||||
type: object
|
||||
properties:
|
||||
uuid:
|
||||
title: Uuid
|
||||
description: Select which transports should be used to notify the user.
|
||||
If none are selected, the notification will only be shown in the authentik
|
||||
UI.
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
minLength: 1
|
||||
mode:
|
||||
title: Mode
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
- webhook_slack
|
||||
- email
|
||||
webhook_url:
|
||||
title: Webhook url
|
||||
type: string
|
||||
send_once:
|
||||
title: Send once
|
||||
description: Only send notification once, for example when sending a
|
||||
webhook into a chat channel.
|
||||
type: boolean
|
||||
readOnly: true
|
||||
uniqueItems: true
|
||||
severity:
|
||||
title: Severity
|
||||
description: Controls which severity level the created notifications will
|
||||
@ -15748,57 +15728,14 @@ definitions:
|
||||
- warning
|
||||
- alert
|
||||
group:
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
properties:
|
||||
group_uuid:
|
||||
title: Group uuid
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
maxLength: 80
|
||||
minLength: 1
|
||||
is_superuser:
|
||||
title: Is superuser
|
||||
description: Users added to this group will be superusers.
|
||||
type: boolean
|
||||
attributes:
|
||||
title: Attributes
|
||||
type: object
|
||||
parent:
|
||||
required:
|
||||
- name
|
||||
- parent
|
||||
type: object
|
||||
properties:
|
||||
group_uuid:
|
||||
title: Group uuid
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
name:
|
||||
title: Name
|
||||
type: string
|
||||
maxLength: 80
|
||||
minLength: 1
|
||||
is_superuser:
|
||||
title: Is superuser
|
||||
description: Users added to this group will be superusers.
|
||||
type: boolean
|
||||
attributes:
|
||||
title: Attributes
|
||||
type: object
|
||||
parent:
|
||||
title: Parent
|
||||
title: Group
|
||||
description: Define which group of users this notification should be sent
|
||||
and shown to. If left empty, Notification won't ben sent.
|
||||
type: string
|
||||
format: uuid
|
||||
x-nullable: true
|
||||
readOnly: true
|
||||
readOnly: true
|
||||
group_obj:
|
||||
$ref: '#/definitions/Group'
|
||||
NotificationTransport:
|
||||
required:
|
||||
- name
|
||||
|
||||
@ -70,7 +70,7 @@ class TestProviderProxy(SeleniumTestCase):
|
||||
authorization_flow=Flow.objects.get(
|
||||
slug="default-provider-authorization-implicit-consent"
|
||||
),
|
||||
internal_host="http://localhost:80",
|
||||
internal_host="http://localhost",
|
||||
external_host="http://localhost:4180",
|
||||
)
|
||||
# Ensure OAuth2 Params are set
|
||||
@ -123,7 +123,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
|
||||
authorization_flow=Flow.objects.get(
|
||||
slug="default-provider-authorization-implicit-consent"
|
||||
),
|
||||
internal_host="http://localhost:80",
|
||||
internal_host="http://localhost",
|
||||
external_host="http://localhost:4180",
|
||||
)
|
||||
# Ensure OAuth2 Params are set
|
||||
|
||||
@ -104,5 +104,5 @@ class OutpostDockerTests(TestCase):
|
||||
self.assertEqual(compose["version"], "3.5")
|
||||
self.assertEqual(
|
||||
compose["services"]["authentik_proxy"]["image"],
|
||||
f"beryju/authentik-proxy:{__version__}",
|
||||
f"ghcr.io/goauthentik/proxy:{__version__}",
|
||||
)
|
||||
|
||||
@ -104,5 +104,5 @@ class TestProxyDocker(TestCase):
|
||||
self.assertEqual(compose["version"], "3.5")
|
||||
self.assertEqual(
|
||||
compose["services"]["authentik_proxy"]["image"],
|
||||
f"beryju/authentik-proxy:{__version__}",
|
||||
f"ghcr.io/goauthentik/proxy:{__version__}",
|
||||
)
|
||||
|
||||
@ -81,7 +81,7 @@ http {
|
||||
location /static/ {
|
||||
expires 31d;
|
||||
add_header Cache-Control "public, no-transform";
|
||||
add_header X-authentik-version "2021.5.1-rc8";
|
||||
add_header X-authentik-version "2021.5.1";
|
||||
add_header Vary X-authentik-version;
|
||||
}
|
||||
|
||||
|
||||
1377
web/package-lock.json
generated
1377
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -35,10 +35,10 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.14.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.13.15",
|
||||
"@babel/plugin-transform-runtime": "^7.13.15",
|
||||
"@babel/preset-env": "^7.14.1",
|
||||
"@babel/core": "^7.14.2",
|
||||
"@babel/plugin-proposal-decorators": "^7.14.2",
|
||||
"@babel/plugin-transform-runtime": "^7.14.2",
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||
"@lingui/cli": "^3.8.10",
|
||||
|
||||
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2021.5.1-rc8";
|
||||
export const VERSION = "2021.5.1";
|
||||
export const PAGE_SIZE = 20;
|
||||
export const EVENT_REFRESH = "ak-refresh";
|
||||
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
|
||||
|
||||
@ -67,7 +67,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
<option value="" ?selected=${this.instance?.group === undefined}>---------</option>
|
||||
${until(new CoreApi(DEFAULT_CONFIG).coreGroupsList({}).then(groups => {
|
||||
return groups.results.map(group => {
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group?.groupUuid === group.pk}>${group.name}</option>`;
|
||||
return html`<option value=${ifDefined(group.pk)} ?selected=${this.instance?.group === group.pk}>${group.name}</option>`;
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
@ -80,7 +80,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
${until(new EventsApi(DEFAULT_CONFIG).eventsTransportsList({}).then(transports => {
|
||||
return transports.results.map(transport => {
|
||||
const selected = Array.from(this.instance?.transports || []).some(su => {
|
||||
return su.uuid == transport.pk;
|
||||
return su == transport.pk;
|
||||
});
|
||||
return html`<option value=${ifDefined(transport.pk)} ?selected=${selected}>${transport.name}</option>`;
|
||||
});
|
||||
|
||||
@ -55,7 +55,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.severity}`,
|
||||
html`${item.group?.name || t`None (rule disabled)`}`,
|
||||
html`${item.groupObj?.name || t`None (rule disabled)`}`,
|
||||
html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit">
|
||||
|
||||
@ -58,14 +58,6 @@ export class FlowViewPage extends LitElement {
|
||||
<ak-tabs>
|
||||
<div slot="page-overview" data-tab-title="${t`Flow Overview`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
<div class="pf-c-card pf-l-gallery__item" style="grid-column-end: span 4;grid-row-end: span 2;">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-flow-diagram flowSlug=${this.flow.slug}>
|
||||
</ak-flow-diagram>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-gallery__item">
|
||||
<div class="pf-c-card__title">${t`Related`}</div>
|
||||
<div class="pf-c-card__body">
|
||||
@ -94,6 +86,14 @@ export class FlowViewPage extends LitElement {
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card pf-l-gallery__item" style="grid-column-end: span 4;grid-row-end: span 2;">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
<ak-flow-diagram flowSlug=${this.flow.slug}>
|
||||
</ak-flow-diagram>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="page-stage-bindings" data-tab-title="${t`Stage Bindings`}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
|
||||
@ -42,13 +42,12 @@ export class OutpostHealthElement extends LitElement {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.outpostHealth.length === 0) {
|
||||
return html`<li>
|
||||
return html`
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<ak-label color=${PFColor.Grey} text=${t`Not available`}></ak-label>
|
||||
</li>
|
||||
</ul>
|
||||
</li>`;
|
||||
</ul>`;
|
||||
}
|
||||
return html`<ul>${this.outpostHealth.map((h) => {
|
||||
return html`<li>
|
||||
|
||||
@ -76,6 +76,40 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
this.plexResources = await new PlexAPIClient(this.plexToken).getServers();
|
||||
}
|
||||
|
||||
renderSettings(): TemplateResult {
|
||||
if (!this.plexToken) {
|
||||
return html`
|
||||
<button class="pf-c-button pf-m-primary" type="button" @click=${() => {
|
||||
this.doAuth();
|
||||
}}>
|
||||
${t`Load servers`}
|
||||
</button>`;
|
||||
}
|
||||
return html`<ak-form-element-horizontal name="allowFriends">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.allowFriends, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Allow friends to authenticate via Plex, even if you don't share any servers`}
|
||||
</label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Allowed servers`}
|
||||
?required=${true}
|
||||
name="allowedServers">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.plexResources?.map(r => {
|
||||
const selected = Array.from(this.instance?.allowedServers || []).some(server => {
|
||||
return server == r.clientIdentifier;
|
||||
});
|
||||
return html`<option value=${r.clientIdentifier} ?selected=${selected}>${r.name}</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select which server a user has to be a member of to be allowed to authenticate.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-form-element-horizontal
|
||||
@ -132,36 +166,7 @@ export class PlexSourceForm extends ModelForm<PlexSource, string> {
|
||||
name="clientId">
|
||||
<input type="text" value="${first(this.instance?.clientId)}" class="pf-c-form-control" required>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal name="allowFriends">
|
||||
<div class="pf-c-check">
|
||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.allowFriends, true)}>
|
||||
<label class="pf-c-check__label">
|
||||
${t`Allow friends to authenticate via Plex, even if you don't share any servers`}
|
||||
</label>
|
||||
</div>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Allowed servers`}
|
||||
?required=${true}
|
||||
name="allowedServers">
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.plexResources?.map(r => {
|
||||
const selected = Array.from(this.instance?.allowedServers || []).some(server => {
|
||||
return server == r.clientIdentifier;
|
||||
});
|
||||
return html`<option value=${r.clientIdentifier} ?selected=${selected}>${r.name}</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">${t`Select which server a user has to be a member of to be allowed to authenticate.`}</p>
|
||||
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
<button class="pf-c-button pf-m-primary" type="button" @click=${() => {
|
||||
this.doAuth();
|
||||
}}>
|
||||
${t`Load servers`}
|
||||
</button>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.renderSettings()}
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
|
||||
@ -70,7 +70,7 @@ export class AuthenticatorValidateStageForm extends ModelForm<AuthenticatorValid
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Not configured action`}
|
||||
?required=${true}
|
||||
name="mode">
|
||||
name="notConfiguredAction">
|
||||
<select class="pf-c-form-control" @change=${(ev: Event) => {
|
||||
const target = ev.target as HTMLSelectElement;
|
||||
if (target.selectedOptions[0].value === AuthenticatorValidateStageNotConfiguredActionEnum.Configure) {
|
||||
|
||||
@ -16,7 +16,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte
|
||||
|
||||
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
|
||||
|
||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.5.1-rc8 >> .env`
|
||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.5.1 >> .env`
|
||||
|
||||
If this is a fresh authentik install run the following commands to generate a password:
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ version: "3.5"
|
||||
|
||||
services:
|
||||
authentik_proxy:
|
||||
image: beryju/authentik-proxy:2021.5.1-rc8
|
||||
image: beryju/authentik-proxy:2021.5.1
|
||||
ports:
|
||||
- 4180:4180
|
||||
- 4443:4443
|
||||
|
||||
@ -14,7 +14,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
name: authentik-outpost-api
|
||||
stringData:
|
||||
authentik_host: "__AUTHENTIK_URL__"
|
||||
@ -29,7 +29,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
ports:
|
||||
@ -54,7 +54,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
selector:
|
||||
@ -62,14 +62,14 @@ spec:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
@ -88,7 +88,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: authentik_host_insecure
|
||||
name: authentik-outpost-api
|
||||
image: beryju/authentik-proxy:2021.5.1-rc8
|
||||
image: beryju/authentik-proxy:2021.5.1
|
||||
name: proxy
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
@ -110,7 +110,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.5.1-rc8
|
||||
app.kubernetes.io/version: 2021.5.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
rules:
|
||||
|
||||
@ -34,7 +34,7 @@ module.exports = {
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
href: "https://github.com/beryju/authentik",
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
label: "GitHub",
|
||||
position: "right",
|
||||
},
|
||||
@ -69,7 +69,7 @@ module.exports = {
|
||||
items: [
|
||||
{
|
||||
label: "GitHub",
|
||||
href: "https://github.com/beryju/authentik",
|
||||
href: "https://github.com/goauthentik/authentik",
|
||||
},
|
||||
{
|
||||
label: "Discord",
|
||||
@ -95,7 +95,7 @@ module.exports = {
|
||||
docs: {
|
||||
id: "docs",
|
||||
sidebarPath: require.resolve("./sidebars.js"),
|
||||
editUrl: "https://github.com/beryju/authentik/edit/master/website/",
|
||||
editUrl: "https://github.com/goauthentik/authentik/edit/master/website/",
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
@ -111,7 +111,7 @@ module.exports = {
|
||||
path: 'developer-docs',
|
||||
routeBasePath: 'developer-docs',
|
||||
sidebarPath: require.resolve('./sidebarsDev.js'),
|
||||
editUrl: "https://github.com/beryju/authentik/edit/master/website/",
|
||||
editUrl: "https://github.com/goauthentik/authentik/edit/master/website/",
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
8103
website/package-lock.json
generated
8103
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
||||
"serve": "docusaurus serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/preset-classic": "2.0.0-alpha.75",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.1.1",
|
||||
"postcss": "^8.2.15",
|
||||
|
||||
Reference in New Issue
Block a user