Merge branch 'master' into inbuilt-proxy

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

# Conflicts:
#	Dockerfile
#	internal/outpost/ak/api.go
#	internal/outpost/ak/api_uag.go
#	internal/outpost/ak/global.go
#	internal/outpost/ldap/api_tls.go
#	internal/outpost/ldap/instance_bind.go
#	internal/outpost/ldap/utils.go
#	internal/outpost/proxy/api_bundle.go
#	outpost/go.mod
#	outpost/go.sum
#	outpost/pkg/ak/cert.go
This commit is contained in:
Jens Langhammer 2021-07-17 12:49:13 +02:00
commit b3159a74e5
61 changed files with 5202 additions and 3714 deletions

View File

@ -3,3 +3,6 @@ static
htmlcov
*.env.yml
**/node_modules
dist/**
build/**
build_docs/**

View File

@ -10,8 +10,16 @@ RUN pip install pipenv && \
pipenv lock -r > requirements.txt && \
pipenv lock -r --dev-only > requirements-dev.txt
# Stage 2: Build web API
FROM openapitools/openapi-generator-cli as web-api-builder
# Stage 2: Build website
FROM node as website-builder
COPY ./website /static/
ENV NODE_ENV=production
RUN cd /static && npm i && npm run build-docs-only
# Stage 3: Build web API
FROM openapitools/openapi-generator-cli as api-builder
COPY ./schema.yml /local/schema.yml
@ -37,7 +45,7 @@ RUN docker-entrypoint.sh generate \
rm -f /local/api/go.mod /local/api/go.sum
# Stage 4: Build webui
FROM node as npm-builder
FROM node as web-builder
COPY ./web /static/
COPY --from=web-api-builder /local/web/api /static/api
@ -46,18 +54,20 @@ ENV NODE_ENV=production
RUN cd /static && npm i && npm run build
# Stage 5: Build go proxy
FROM golang:1.16.5 AS builder
FROM golang:1.16.6 AS builder
WORKDIR /work
COPY --from=npm-builder /static/robots.txt /work/web/robots.txt
COPY --from=npm-builder /static/security.txt /work/web/security.txt
COPY --from=npm-builder /static/dist/ /work/web/dist/
COPY --from=npm-builder /static/authentik/ /work/web/authentik/
COPY --from=web-builder /static/robots.txt /work/web/robots.txt
COPY --from=web-builder /static/security.txt /work/web/security.txt
COPY --from=web-builder /static/dist/ /work/web/dist/
COPY --from=web-builder /static/authentik/ /work/web/authentik/
COPY --from=website-builder /static/help/ /work/website/help/
COPY --from=go-api-builder /local/api api
COPY ./cmd /work/cmd
COPY ./web/static.go /work/web/static.go
COPY ./website/static.go /work/website/static.go
COPY ./internal /work/internal
COPY ./go.mod /work/go.mod
COPY ./go.sum /work/go.sum

View File

@ -47,6 +47,7 @@ xmlsec = "*"
duo-client = "*"
ua-parser = "*"
deepmerge = "*"
colorama = "*"
[requires]
python_version = "3.9"

122
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "f90d9fb4713eaf9c5ffe6a3858e64843670f79ab5007e7debf914c1f094c8d63"
"sha256": "e4f2e57bd5c709809515ab2b95eb3f5fa337d4a9334f4110a24bf28c3f9d5f8f"
},
"pipfile-spec": 6,
"requires": {
@ -122,19 +122,19 @@
},
"boto3": {
"hashes": [
"sha256:10122ff0f942d7400b18b726edaead20600178f8246cb21b40420073350613b5",
"sha256:484bba256137c2d2f8351175553dee0e888e8bd5872f5406c8984e02715acf4d"
"sha256:054e347824064b7cd77616f35596420eb4f6aca049ecc131a2aec23bcf4cf6ba",
"sha256:22802b3b4806cafff41ed591e578f048789a8dd0deeff07d055cd6f59a7c6076"
],
"index": "pypi",
"version": "==1.17.108"
"version": "==1.18.0"
},
"botocore": {
"hashes": [
"sha256:7667ef69001708afa796d2e79910230715e8542a910820581bf4623a5d3b0d47",
"sha256:f4686d2ccf68dfcd90d2591695938fd0906ae0a7121f792d193b0f000a5d8872"
"sha256:0d46f0addc1d7930c42115a814e5e0c3399666f6d11c1e58d9ec726dfed02ada",
"sha256:84ea51660c758416f75ac612374156f6423a1d1ade449d38944de6eb8493cca2"
],
"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.108"
"markers": "python_version >= '3.6'",
"version": "==1.21.0"
},
"cachetools": {
"hashes": [
@ -189,8 +189,10 @@
"sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202",
"sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5",
"sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548",
"sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a",
"sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f",
"sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20",
"sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218",
"sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c",
"sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e",
"sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56",
@ -202,6 +204,7 @@
"sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346",
"sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b",
"sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e",
"sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534",
"sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb",
"sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0",
"sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156",
@ -215,21 +218,23 @@
"sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd",
"sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728",
"sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7",
"sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca",
"sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99",
"sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf",
"sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e",
"sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c",
"sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5",
"sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"
],
"version": "==1.14.6"
},
"channels": {
"hashes": [
"sha256:056b72e51080a517a0f33a0a30003e03833b551d75394d6636c885d4edb8188f",
"sha256:3f15bdd2138bb4796e76ea588a0a344b12a7964ea9b2e456f992fddb988a4317"
"sha256:0ff0422b4224d10efac76e451575517f155fe7c97d369b5973b116f22eeaf86c",
"sha256:fdd9a94987a23d8d7ebd97498ed8b8cc83163f37e53fc6c85098aba7a3bb8b75"
],
"index": "pypi",
"version": "==3.0.3"
"version": "==3.0.4"
},
"channels-redis": {
"hashes": [
@ -247,6 +252,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"charset-normalizer": {
"hashes": [
"sha256:3c502a35807c9df35697b0f44b1d65008f83071ff29c69677c7c22573bc5a45a",
"sha256:951567c2f7433a70ab63f1be67e5ee05d3925d9423306ecb71a3b272757bcc95"
],
"markers": "python_version >= '3'",
"version": "==2.0.2"
},
"click": {
"hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
@ -275,6 +288,14 @@
],
"version": "==0.2.0"
},
"colorama": {
"hashes": [
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
"index": "pypi",
"version": "==0.4.4"
},
"constantly": {
"hashes": [
"sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35",
@ -464,11 +485,11 @@
},
"google-auth": {
"hashes": [
"sha256:9266252e11393943410354cf14a77bcca24dd2ccd9c4e1aef23034fe0fbae630",
"sha256:c7c215c74348ef24faef2f7b62f6d8e6b38824fe08b1e7b7b09a02d397eda7b3"
"sha256:9bd25d835c01ca3dd9045ea0b50f036e3fe3868ee2c5a9773a661925bbdf46ca",
"sha256:a756a33978fac611e4f00b69715b80e35467c30cc6262132d29d33a0e398ac55"
],
"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.1"
"version": "==1.33.0"
},
"gunicorn": {
"hashes": [
@ -562,10 +583,10 @@
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"version": "==2.10"
"version": "==3.2"
},
"incremental": {
"hashes": [
@ -966,11 +987,11 @@
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
"version": "==2.8.2"
},
"python-dotenv": {
"hashes": [
@ -1031,11 +1052,11 @@
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==2.26.0"
},
"requests-oauthlib": {
"hashes": [
@ -1056,10 +1077,11 @@
},
"s3transfer": {
"hashes": [
"sha256:9b3752887a2880690ce628bc263d6d13a3864083aeacff4890c1c9839a5eb0bc",
"sha256:cb022f4b16551edebbb31a377d3f09600dbada7363d8c5db7976e7f47732e1b2"
"sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c",
"sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803"
],
"version": "==0.4.2"
"markers": "python_version >= '3.6'",
"version": "==0.5.0"
},
"sentry-sdk": {
"hashes": [
@ -1197,18 +1219,18 @@
},
"uvloop": {
"hashes": [
"sha256:114543c84e95df1b4ff546e6e3a27521580466a30127f12172a3278172ad68bc",
"sha256:19fa1d56c91341318ac5d417e7b61c56e9a41183946cc70c411341173de02c69",
"sha256:2bb0624a8a70834e54dde8feed62ed63b50bad7a1265c40d6403a2ac447bce01",
"sha256:42eda9f525a208fbc4f7cecd00fa15c57cc57646c76632b3ba2fe005004f051d",
"sha256:44cac8575bf168601424302045234d74e3561fbdbac39b2b54cc1d1d00b70760",
"sha256:6de130d0cb78985a5d080e323b86c5ecaf3af82f4890492c05981707852f983c",
"sha256:7ae39b11a5f4cec1432d706c21ecc62f9e04d116883178b09671aa29c46f7a47",
"sha256:90e56f17755e41b425ad19a08c41dc358fa7bf1226c0f8e54d4d02d556f7af7c",
"sha256:b45218c99795803fb8bdbc9435ff7f54e3a591b44cd4c121b02fa83affb61c7c",
"sha256:e5e5f855c9bf483ee6cd1eb9a179b740de80cb0ae2988e3fa22309b78e2ea0e7"
"sha256:0de811931e90ae2da9e19ce70ffad73047ab0c1dba7c6e74f9ae1a3aabeb89bd",
"sha256:1ff05116ede1ebdd81802df339e5b1d4cab1dfbd99295bf27e90b4cec64d70e9",
"sha256:2d8ffe44ae709f839c54bacf14ed283f41bee90430c3b398e521e10f8d117b3a",
"sha256:5cda65fc60a645470b8525ce014516b120b7057b576fa876cdfdd5e60ab1efbb",
"sha256:63a3288abbc9c8ee979d7e34c34e780b2fbab3e7e53d00b6c80271119f277399",
"sha256:7522df4e45e4f25b50adbbbeb5bb9847495c438a628177099d2721f2751ff825",
"sha256:7f4b8a905df909a407c5791fb582f6c03b0d3b491ecdc1cdceaefbc9bf9e08f6",
"sha256:905f0adb0c09c9f44222ee02f6b96fd88b493478fffb7a345287f9444e926030",
"sha256:ae2b325c0f6d748027f7463077e457006b4fdb35a8788f01754aadba825285ee",
"sha256:e71fb9038bfcd7646ca126c5ef19b17e48d4af9e838b2bcfda7a9f55a6552a32"
],
"version": "==0.15.2"
"version": "==0.15.3"
},
"vine": {
"hashes": [
@ -1459,13 +1481,13 @@
],
"version": "==2021.5.30"
},
"chardet": {
"charset-normalizer": {
"hashes": [
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
"sha256:3c502a35807c9df35697b0f44b1d65008f83071ff29c69677c7c22573bc5a45a",
"sha256:951567c2f7433a70ab63f1be67e5ee05d3925d9423306ecb71a3b272757bcc95"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
"markers": "python_version >= '3'",
"version": "==2.0.2"
},
"click": {
"hashes": [
@ -1559,10 +1581,10 @@
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"version": "==2.10"
"version": "==3.2"
},
"iniconfig": {
"hashes": [
@ -1790,11 +1812,11 @@
},
"requests": {
"hashes": [
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.1"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==2.26.0"
},
"requests-mock": {
"hashes": [

View File

@ -49,7 +49,7 @@ class ConfigView(APIView):
caps.append(Capabilities.CAN_GEO_IP)
if SERVICE_HOST_ENV_NAME in environ:
# Running in k8s, only s3 backup is supported
if CONFIG.y("postgresql.s3_backup"):
if CONFIG.y_bool("postgresql.s3_backup"):
caps.append(Capabilities.CAN_BACKUP)
else:
# Running in compose, backup is always supported

View File

@ -0,0 +1,38 @@
"""Sentry tunnel"""
from json import loads
from django.conf import settings
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.views.generic.base import View
from requests import post
from requests.exceptions import RequestException
from authentik.lib.config import CONFIG
class SentryTunnelView(View):
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
# Only allow usage of this endpoint when error reporting is enabled
if not CONFIG.y_bool("error_reporting.enabled", False):
return HttpResponse(status=400)
# Body is 2 json objects separated by \n
full_body = request.body
header = loads(full_body.splitlines()[0])
# Check that the DSN is what we expect
dsn = header.get("dsn", "")
if dsn != settings.SENTRY_DSN:
return HttpResponse(status=400)
response = post(
"https://sentry.beryju.org/api/8/envelope/",
data=full_body,
headers={"Content-Type": "application/octet-stream"},
)
try:
response.raise_for_status()
except RequestException:
return HttpResponse(status=500)
return HttpResponse(status=response.status_code)

View File

@ -1,5 +1,6 @@
"""api v2 urls"""
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.views import SpectacularAPIView
from rest_framework import routers
@ -10,6 +11,7 @@ from authentik.admin.api.tasks import TaskViewSet
from authentik.admin.api.version import VersionView
from authentik.admin.api.workers import WorkerView
from authentik.api.v2.config import ConfigView
from authentik.api.v2.sentry import SentryTunnelView
from authentik.api.views import APIBrowserView
from authentik.core.api.applications import ApplicationViewSet
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
@ -235,6 +237,7 @@ urlpatterns = (
FlowExecutorView.as_view(),
name="flow-executor",
),
path("sentry/", csrf_exempt(SentryTunnelView.as_view()), name="sentry"),
path("schema/", SpectacularAPIView.as_view(), name="schema"),
]
)

View File

@ -1,24 +1,60 @@
"""Groups API Viewset"""
from django.db.models.query import QuerySet
from rest_framework.fields import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.fields import BooleanField, CharField, JSONField
from rest_framework.serializers import ListSerializer, ModelSerializer
from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import is_dict
from authentik.core.models import Group
from authentik.core.models import Group, User
class GroupMemberSerializer(ModelSerializer):
"""Stripped down user serializer to show relevant users for groups"""
is_superuser = BooleanField(read_only=True)
avatar = CharField(read_only=True)
attributes = JSONField(validators=[is_dict], required=False)
uid = CharField(read_only=True)
class Meta:
model = User
fields = [
"pk",
"username",
"name",
"is_active",
"last_login",
"is_superuser",
"email",
"avatar",
"attributes",
"uid",
]
class GroupSerializer(ModelSerializer):
"""Group Serializer"""
attributes = JSONField(validators=[is_dict], required=False)
users_obj = ListSerializer(
child=GroupMemberSerializer(), read_only=True, source="users", required=False
)
class Meta:
model = Group
fields = ["pk", "name", "is_superuser", "parent", "users", "attributes"]
fields = [
"pk",
"name",
"is_superuser",
"parent",
"users",
"attributes",
"users_obj",
]
class GroupViewSet(UsedByMixin, ModelViewSet):

View File

@ -12,7 +12,7 @@ from authentik.api.decorators import permission_required
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import Token, TokenIntents
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents
from authentik.events.models import Event, EventAction
from authentik.managed.api import ManagedSerializer
@ -61,11 +61,19 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
"intent",
"user__username",
"description",
"expires",
"expiring",
]
ordering = ["expires"]
def perform_create(self, serializer: TokenSerializer):
serializer.save(user=self.request.user, intent=TokenIntents.INTENT_API)
serializer.save(
user=self.request.user,
intent=TokenIntents.INTENT_API,
expiring=self.request.user.attributes.get(
USER_ATTRIBUTE_TOKEN_EXPIRING, True
),
)
@permission_required("authentik_core.view_token_key")
@extend_schema(

View File

@ -37,6 +37,7 @@ LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources"
USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec
GRAVATAR_URL = "https://secure.gravatar.com"
DEFAULT_AVATAR = static("dist/assets/images/user_default.png")
@ -380,6 +381,13 @@ class ExpiringModel(models.Model):
expires = models.DateTimeField(default=default_token_duration)
expiring = models.BooleanField(default=True)
def expire_action(self, *args, **kwargs):
"""Handler which is called when this object is expired. By
default the object is deleted. This is less efficient compared
to bulk deleting objects, but classes like Token() need to change
values instead of being deleted."""
return self.delete(*args, **kwargs)
@classmethod
def filter_not_expired(cls, **kwargs) -> QuerySet:
"""Filer for tokens which are not expired yet or are not expiring,
@ -424,6 +432,18 @@ class Token(ManagedModel, ExpiringModel):
user = models.ForeignKey("User", on_delete=models.CASCADE, related_name="+")
description = models.TextField(default="", blank=True)
def expire_action(self, *args, **kwargs):
"""Handler which is called when this object is expired."""
from authentik.events.models import Event, EventAction
self.key = default_token_key()
self.save(*args, **kwargs)
Event.new(
action=EventAction.SECRET_ROTATE,
token=self,
message=f"Token {self.identifier}'s secret was rotated.",
).save()
def __str__(self):
description = f"{self.identifier}"
if self.expiring:

View File

@ -26,14 +26,16 @@ def clean_expired_models(self: MonitoredTask):
messages = []
for cls in ExpiringModel.__subclasses__():
cls: ExpiringModel
amount, _ = (
objects = (
cls.objects.all()
.exclude(expiring=False)
.exclude(expiring=True, expires__gt=now())
.delete()
)
LOGGER.debug("Deleted expired models", model=cls, amount=amount)
messages.append(f"Deleted {amount} expired {cls._meta.verbose_name_plural}")
for obj in objects:
obj.expire_action()
amount = objects.count()
LOGGER.debug("Expired models", model=cls, amount=amount)
messages.append(f"Expired {amount} {cls._meta.verbose_name_plural}")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

View File

@ -1,18 +0,0 @@
"""authentik core task tests"""
from django.test import TestCase
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from authentik.core.models import Token
from authentik.core.tasks import clean_expired_models
class TestTasks(TestCase):
"""Test Tasks"""
def test_token_cleanup(self):
"""Test Token cleanup task"""
Token.objects.create(expires=now(), user=get_anonymous_user())
self.assertEqual(Token.objects.all().count(), 1)
clean_expired_models.delay().get()
self.assertEqual(Token.objects.all().count(), 0)

View File

@ -1,8 +1,16 @@
"""Test token API"""
from django.urls.base import reverse
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase
from authentik.core.models import Token, TokenIntents, User
from authentik.core.models import (
USER_ATTRIBUTE_TOKEN_EXPIRING,
Token,
TokenIntents,
User,
)
from authentik.core.tasks import clean_expired_models
class TestTokenAPI(APITestCase):
@ -22,3 +30,25 @@ class TestTokenAPI(APITestCase):
token = Token.objects.get(identifier="test-token")
self.assertEqual(token.user, self.user)
self.assertEqual(token.intent, TokenIntents.INTENT_API)
self.assertEqual(token.expiring, True)
def test_token_create_non_expiring(self):
"""Test token creation endpoint"""
self.user.attributes[USER_ATTRIBUTE_TOKEN_EXPIRING] = False
self.user.save()
response = self.client.post(
reverse("authentik_api:token-list"), {"identifier": "test-token"}
)
self.assertEqual(response.status_code, 201)
token = Token.objects.get(identifier="test-token")
self.assertEqual(token.user, self.user)
self.assertEqual(token.intent, TokenIntents.INTENT_API)
self.assertEqual(token.expiring, False)
def test_token_expire(self):
"""Test Token expire task"""
token: Token = Token.objects.create(expires=now(), user=get_anonymous_user())
key = token.key
clean_expired_models.delay().get()
token.refresh_from_db()
self.assertNotEqual(key, token.key)

View File

@ -0,0 +1,47 @@
# Generated by Django 3.2.5 on 2021-07-14 19:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_events", "0016_add_tenant"),
]
operations = [
migrations.AlterField(
model_name="event",
name="action",
field=models.TextField(
choices=[
("login", "Login"),
("login_failed", "Login Failed"),
("logout", "Logout"),
("user_write", "User Write"),
("suspicious_request", "Suspicious Request"),
("password_set", "Password Set"),
("secret_view", "Secret View"),
("secret_rotate", "Secret Rotate"),
("invitation_used", "Invite Used"),
("authorize_application", "Authorize Application"),
("source_linked", "Source Linked"),
("impersonation_started", "Impersonation Started"),
("impersonation_ended", "Impersonation Ended"),
("policy_execution", "Policy Execution"),
("policy_exception", "Policy Exception"),
("property_mapping_exception", "Property Mapping Exception"),
("system_task_execution", "System Task Execution"),
("system_task_exception", "System Task Exception"),
("system_exception", "System Exception"),
("configuration_error", "Configuration Error"),
("model_created", "Model Created"),
("model_updated", "Model Updated"),
("model_deleted", "Model Deleted"),
("email_sent", "Email Sent"),
("update_available", "Update Available"),
("custom_", "Custom Prefix"),
]
),
),
]

View File

@ -62,6 +62,7 @@ class EventAction(models.TextChoices):
PASSWORD_SET = "password_set" # noqa # nosec
SECRET_VIEW = "secret_view" # noqa # nosec
SECRET_ROTATE = "secret_rotate" # noqa # nosec
INVITE_USED = "invitation_used"

View File

@ -13,7 +13,10 @@ web:
redis:
host: localhost
port: 6379
password: ''
tls: false
tls_reqs: "none"
cache_db: 0
message_queue_db: 1
ws_db: 2

View File

@ -0,0 +1,49 @@
# Generated by Django 3.2.5 on 2021-07-14 19:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0017_alter_eventmatcherpolicy_action"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="action",
field=models.TextField(
blank=True,
choices=[
("login", "Login"),
("login_failed", "Login Failed"),
("logout", "Logout"),
("user_write", "User Write"),
("suspicious_request", "Suspicious Request"),
("password_set", "Password Set"),
("secret_view", "Secret View"),
("secret_rotate", "Secret Rotate"),
("invitation_used", "Invite Used"),
("authorize_application", "Authorize Application"),
("source_linked", "Source Linked"),
("impersonation_started", "Impersonation Started"),
("impersonation_ended", "Impersonation Ended"),
("policy_execution", "Policy Execution"),
("policy_exception", "Policy Exception"),
("property_mapping_exception", "Property Mapping Exception"),
("system_task_execution", "System Task Execution"),
("system_task_exception", "System Task Exception"),
("system_exception", "System Exception"),
("configuration_error", "Configuration Error"),
("model_created", "Model Created"),
("model_updated", "Model Updated"),
("model_deleted", "Model Deleted"),
("email_sent", "Email Sent"),
("update_available", "Update Available"),
("custom_", "Custom Prefix"),
],
help_text="Match created events with this action type. When left empty, all action types will be matched.",
),
),
]

View File

@ -21,12 +21,15 @@ def update_score(request: HttpRequest, username: str, amount: int):
"""Update score for IP and User"""
remote_ip = get_client_ip(request)
try:
# We only update the cache here, as its faster than writing to the DB
cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0, CACHE_TIMEOUT)
cache.incr(CACHE_KEY_IP_PREFIX + remote_ip, amount)
cache.get_or_set(CACHE_KEY_USER_PREFIX + username, 0, CACHE_TIMEOUT)
cache.incr(CACHE_KEY_USER_PREFIX + username, amount)
except ValueError as exc:
LOGGER.warning("failed to set reputation", exc=exc)
LOGGER.debug("Updated score", amount=amount, for_user=username, for_ip=remote_ip)

View File

@ -17,6 +17,10 @@ class LDAPProviderSerializer(ProviderSerializer):
fields = ProviderSerializer.Meta.fields + [
"base_dn",
"search_group",
"certificate",
"tls_server_name",
"uid_start_number",
"gid_start_number",
]
@ -44,6 +48,10 @@ class LDAPOutpostConfigSerializer(ModelSerializer):
"bind_flow_slug",
"application_slug",
"search_group",
"certificate",
"tls_server_name",
"uid_start_number",
"gid_start_number",
]

View File

@ -11,4 +11,5 @@ class LDAPDockerController(DockerController):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(389, "ldap", "tcp", 3389),
DeploymentPort(636, "ldaps", "tcp", 6636),
]

View File

@ -11,4 +11,5 @@ class LDAPKubernetesController(KubernetesController):
super().__init__(outpost, connection)
self.deployment_ports = [
DeploymentPort(389, "ldap", "tcp", 3389),
DeploymentPort(636, "ldaps", "tcp", 6636),
]

View File

@ -0,0 +1,30 @@
# Generated by Django 3.2.5 on 2021-07-13 11:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0002_create_self_signed_kp"),
("authentik_providers_ldap", "0002_ldapprovider_search_group"),
]
operations = [
migrations.AddField(
model_name="ldapprovider",
name="certificate",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_crypto.certificatekeypair",
),
),
migrations.AddField(
model_name="ldapprovider",
name="tls_server_name",
field=models.TextField(blank=True, default=""),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 3.2.5 on 2021-07-13 21:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_providers_ldap", "0003_auto_20210713_1138"),
]
operations = [
migrations.AddField(
model_name="ldapprovider",
name="gid_start_number",
field=models.IntegerField(
default=4000,
help_text="The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber",
),
),
migrations.AddField(
model_name="ldapprovider",
name="uid_start_number",
field=models.IntegerField(
default=2000,
help_text="The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber",
),
),
]

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from authentik.core.models import Group, Provider
from authentik.crypto.models import CertificateKeyPair
from authentik.outposts.models import OutpostModel
@ -28,6 +29,36 @@ class LDAPProvider(OutpostModel, Provider):
),
)
tls_server_name = models.TextField(
default="",
blank=True,
)
certificate = models.ForeignKey(
CertificateKeyPair,
on_delete=models.SET_NULL,
null=True,
blank=True,
)
uid_start_number = models.IntegerField(
default=2000,
help_text=_(
"The start for uidNumbers, this number is added to the user.Pk to make sure that the "
"numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't "
"collide with local users uidNumber"
),
)
gid_start_number = models.IntegerField(
default=4000,
help_text=_(
"The start for gidNumbers, this number is added to a number generated from the "
"group.Pk to make sure that the numbers aren't too low for POSIX groups. Default "
"is 4000 to ensure that we don't collide with local groups or users "
"primary groups gidNumber"
),
)
@property
def launch_url(self) -> Optional[str]:
"""LDAP never has a launch URL"""

View File

@ -188,12 +188,19 @@ REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
REDIS_PROTOCOL_PREFIX = "redis://"
REDIS_CELERY_TLS_REQUIREMENTS = ""
if CONFIG.y_bool("redis.tls", False):
REDIS_PROTOCOL_PREFIX = "rediss://"
REDIS_CELERY_TLS_REQUIREMENTS = f"?ssl_cert_reqs={CONFIG.y('redis.tls_reqs')}"
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": (
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:6379"
f"/{CONFIG.y('redis.cache_db')}"
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.cache_db')}"
),
"TIMEOUT": int(CONFIG.y("redis.cache_timeout", 300)),
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
@ -252,8 +259,9 @@ CHANNEL_LAYERS = {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:6379"
f"/{CONFIG.y('redis.ws_db')}"
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.ws_db')}"
],
},
},
@ -331,12 +339,16 @@ CELERY_BEAT_SCHEDULE = {
CELERY_TASK_CREATE_MISSING_QUEUES = True
CELERY_TASK_DEFAULT_QUEUE = "authentik"
CELERY_BROKER_URL = (
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
f":6379/{CONFIG.y('redis.message_queue_db')}"
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.message_queue_db')}"
f"{REDIS_CELERY_TLS_REQUIREMENTS}"
)
CELERY_RESULT_BACKEND = (
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}"
f":6379/{CONFIG.y('redis.message_queue_db')}"
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.message_queue_db')}"
f"{REDIS_CELERY_TLS_REQUIREMENTS}"
)
# Database backup
@ -364,11 +376,12 @@ if CONFIG.y("postgresql.s3_backup"):
)
# Sentry integration
SENTRY_DSN = "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
_ERROR_REPORTING = CONFIG.y_bool("error_reporting.enabled", False)
if _ERROR_REPORTING:
# pylint: disable=abstract-class-instantiated
sentry_init(
dsn="https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
dsn=SENTRY_DSN,
integrations=[
DjangoIntegration(transaction_style="function_name"),
CeleryIntegration(),

View File

@ -46,7 +46,7 @@ func main() {
for {
go attemptStartBackend(g, ex)
ws.Start()
go attemptProxyStart(u, ex)
// go attemptProxyStart(u, ex)
<-ex
log.WithField("logger", "authentik").Debug("shutting down webserver")

View File

@ -12,8 +12,8 @@ import (
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/recws-org/recws"
"goauthentik.io/internal/constants"
"goauthentik.io/api"
"goauthentik.io/internal/constants"
log "github.com/sirupsen/logrus"
)
@ -40,10 +40,11 @@ type APIController struct {
// NewAPIController initialise new API Controller instance from URL and API token
func NewAPIController(akURL url.URL, token string) *APIController {
config := api.NewConfiguration()
config.UserAgent = constants.OutpostUserAgent()
config.Host = akURL.Host
config.Scheme = akURL.Scheme
config.HTTPClient = &http.Client{
Transport: SetUserAgent(GetTLSTransport(), constants.OutpostUserAgent()),
Transport: GetTLSTransport(),
}
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))

View File

@ -1,20 +0,0 @@
package ak
import "net/http"
func SetUserAgent(inner http.RoundTripper, userAgent string) http.RoundTripper {
return &addUGA{
inner: inner,
Agent: userAgent,
}
}
type addUGA struct {
inner http.RoundTripper
Agent string
}
func (ug *addUGA) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Set("User-Agent", ug.Agent)
return ug.inner.RoundTrip(r)
}

View File

@ -1,6 +1,8 @@
package ak
import (
"context"
"crypto/tls"
"net/http"
"os"
"strings"
@ -9,6 +11,7 @@ import (
"github.com/getsentry/sentry-go"
httptransport "github.com/go-openapi/runtime/client"
log "github.com/sirupsen/logrus"
"goauthentik.io/api"
"goauthentik.io/internal/constants"
)
@ -66,3 +69,21 @@ func GetTLSTransport() http.RoundTripper {
}
return tlsTransport
}
// ParseCertificate Load certificate from Keyepair UUID and parse it into a go Certificate
func ParseCertificate(kpUuid string, cryptoApi *api.CryptoApiService) (*tls.Certificate, error) {
cert, _, err := cryptoApi.CryptoCertificatekeypairsViewCertificateRetrieve(context.Background(), kpUuid).Execute()
if err != nil {
return nil, err
}
key, _, err := cryptoApi.CryptoCertificatekeypairsViewPrivateKeyRetrieve(context.Background(), kpUuid).Execute()
if err != nil {
return nil, err
}
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
if err != nil {
return nil, err
}
return &x509cert, nil
}

View File

@ -2,6 +2,7 @@ package ldap
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
@ -10,6 +11,7 @@ import (
"github.com/go-openapi/strfmt"
log "github.com/sirupsen/logrus"
"goauthentik.io/outpost/pkg/ak"
)
func (ls *LDAPServer) Refresh() error {
@ -24,6 +26,7 @@ func (ls *LDAPServer) Refresh() error {
for idx, provider := range outposts.Results {
userDN := strings.ToLower(fmt.Sprintf("ou=users,%s", *provider.BaseDn))
groupDN := strings.ToLower(fmt.Sprintf("ou=groups,%s", *provider.BaseDn))
logger := log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name)
providers[idx] = &ProviderInstance{
BaseDN: *provider.BaseDn,
GroupDN: groupDN,
@ -35,6 +38,19 @@ func (ls *LDAPServer) Refresh() error {
boundUsers: make(map[string]UserFlags),
s: ls,
log: log.WithField("logger", "authentik.outpost.ldap").WithField("provider", provider.Name),
tlsServerName: provider.TlsServerName,
uidStartNumber: *provider.UidStartNumber,
gidStartNumber: *provider.GidStartNumber,
}
if provider.Certificate.Get() != nil {
logger.WithField("provider", provider.Name).Debug("Enabling TLS")
cert, err := ak.ParseCertificate(*provider.Certificate.Get(), ls.ac.Client.CryptoApi)
if err != nil {
logger.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch certificate")
} else {
providers[idx].cert = cert
logger.WithField("provider", provider.Name).Debug("Loaded certificates")
}
}
}
ls.providers = providers
@ -58,9 +74,30 @@ func (ls *LDAPServer) StartLDAPServer() error {
return ls.s.ListenAndServe(listen)
}
func (ls *LDAPServer) StartLDAPTLSServer() error {
listen := "0.0.0.0:6636"
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
GetCertificate: ls.getCertificates,
}
ln, err := tls.Listen("tcp", listen, tlsConfig)
if err != nil {
ls.log.Fatalf("FATAL: listen (%s) failed - %s", listen, err)
}
ls.log.WithField("listen", listen).Info("Starting ldap tls server")
err = ls.s.Serve(ln)
if err != nil {
return err
}
ls.log.Printf("closing %s", ln.Addr())
return ls.s.ListenAndServe(listen)
}
func (ls *LDAPServer) Start() error {
wg := sync.WaitGroup{}
wg.Add(2)
wg.Add(3)
go func() {
defer wg.Done()
err := ls.StartHTTPServer()
@ -75,24 +112,13 @@ func (ls *LDAPServer) Start() error {
panic(err)
}
}()
go func() {
defer wg.Done()
err := ls.StartLDAPTLSServer()
if err != nil {
panic(err)
}
}()
wg.Wait()
return nil
}
type transport struct {
headers map[string]string
inner http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
for key, value := range t.headers {
req.Header.Add(key, value)
}
return t.inner.RoundTrip(req)
}
func newTransport(inner http.RoundTripper, headers map[string]string) *transport {
return &transport{
inner: inner,
headers: headers,
}
}

View File

@ -0,0 +1,23 @@
package ldap
import "crypto/tls"
func (ls *LDAPServer) getCertificates(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
if len(ls.providers) == 1 {
if ls.providers[0].cert != nil {
ls.log.WithField("server-name", info.ServerName).Debug("We only have a single provider, using their cert")
return ls.providers[0].cert, nil
}
}
for _, provider := range ls.providers {
if provider.tlsServerName == &info.ServerName {
if provider.cert == nil {
ls.log.WithField("server-name", info.ServerName).Debug("Handler does not have a certificate")
return ls.defaultCert, nil
}
return provider.cert, nil
}
}
ls.log.WithField("server-name", info.ServerName).Debug("Fallback to default cert")
return ls.defaultCert, nil
}

View File

@ -54,12 +54,12 @@ func (pi *ProviderInstance) Bind(username string, bindDN, bindPW string, conn ne
config := api.NewConfiguration()
config.Host = pi.s.ac.Client.GetConfig().Host
config.Scheme = pi.s.ac.Client.GetConfig().Scheme
config.UserAgent = constants.OutpostUserAgent()
config.HTTPClient = &http.Client{
Jar: jar,
Transport: newTransport(ak.SetUserAgent(ak.GetTLSTransport(), constants.OutpostUserAgent()), map[string]string{
"X-authentik-remote-ip": host,
}),
Transport: ak.GetTLSTransport(),
}
config.AddDefaultHeader("X-authentik-remote-ip", host)
// create the API client, with the transport
apiClient := api.NewAPIClient(config)

View File

@ -54,8 +54,18 @@ func (pi *ProviderInstance) Search(bindDN string, searchReq ldap.SearchRequest,
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
}
pi.log.WithField("count", len(groups.Results)).Trace("Got results from API")
for _, g := range groups.Results {
entries = append(entries, pi.GroupEntry(g))
entries = append(entries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g)))
}
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute()
if err != nil {
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
}
for _, u := range users.Results {
entries = append(entries, pi.GroupEntry(pi.APIUserToLDAPGroup(u)))
}
case UserObjectClass, "":
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute()
@ -96,6 +106,14 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
Name: "objectClass",
Values: []string{UserObjectClass, "organizationalPerson", "goauthentik.io/ldap/user"},
},
{
Name: "uidNumber",
Values: []string{pi.GetUidNumber(u)},
},
{
Name: "gidNumber",
Values: []string{pi.GetUidNumber(u)},
},
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "memberOf", Values: pi.GroupsForUser(u)})
@ -109,28 +127,45 @@ func (pi *ProviderInstance) UserEntry(u api.User) *ldap.Entry {
attrs = append(attrs, AKAttrsToLDAP(u.Attributes)...)
dn := fmt.Sprintf("cn=%s,%s", u.Username, pi.UserDN)
dn := pi.GetUserDN(u.Username)
return &ldap.Entry{DN: dn, Attributes: attrs}
}
func (pi *ProviderInstance) GroupEntry(g api.Group) *ldap.Entry {
func (pi *ProviderInstance) GroupEntry(g LDAPGroup) *ldap.Entry {
attrs := []*ldap.EntryAttribute{
{
Name: "cn",
Values: []string{g.Name},
Values: []string{g.cn},
},
{
Name: "uid",
Values: []string{string(g.Pk)},
Values: []string{g.uid},
},
{
Name: "objectClass",
Values: []string{GroupObjectClass, "goauthentik.io/ldap/group"},
Name: "gidNumber",
Values: []string{g.gidNumber},
},
}
attrs = append(attrs, AKAttrsToLDAP(g.Attributes)...)
dn := pi.GetGroupDN(g)
return &ldap.Entry{DN: dn, Attributes: attrs}
if g.isVirtualGroup {
attrs = append(attrs, &ldap.EntryAttribute{
Name: "objectClass",
Values: []string{GroupObjectClass, "goauthentik.io/ldap/group", "goauthentik.io/ldap/virtual-group"},
})
} else {
attrs = append(attrs, &ldap.EntryAttribute{
Name: "objectClass",
Values: []string{GroupObjectClass, "goauthentik.io/ldap/group"},
})
}
attrs = append(attrs, &ldap.EntryAttribute{Name: "member", Values: g.member})
attrs = append(attrs, &ldap.EntryAttribute{Name: "goauthentik.io/ldap/superuser", Values: []string{BoolToString(g.isSuperuser)}})
if g.akAttributes != nil {
attrs = append(attrs, AKAttrsToLDAP(g.akAttributes)...)
}
return &ldap.Entry{DN: g.dn, Attributes: attrs}
}

View File

@ -1,6 +1,7 @@
package ldap
import (
"crypto/tls"
"sync"
"github.com/go-openapi/strfmt"
@ -25,9 +26,15 @@ type ProviderInstance struct {
s *LDAPServer
log *log.Entry
tlsServerName *string
cert *tls.Certificate
searchAllowedGroups []*strfmt.UUID
boundUsersMutex sync.RWMutex
boundUsers map[string]UserFlags
uidStartNumber int32
gidStartNumber int32
}
type UserFlags struct {
@ -39,10 +46,21 @@ type LDAPServer struct {
s *ldap.Server
log *log.Entry
ac *ak.APIController
defaultCert *tls.Certificate
providers []*ProviderInstance
}
type LDAPGroup struct {
dn string
cn string
uid string
gidNumber string
member []string
isSuperuser bool
isVirtualGroup bool
akAttributes interface{}
}
func NewServer(ac *ak.APIController) *LDAPServer {
s := ldap.NewServer()
s.EnforceLDAP = true
@ -52,6 +70,11 @@ func NewServer(ac *ak.APIController) *LDAPServer {
ac: ac,
providers: []*ProviderInstance{},
}
defaultCert, err := ak.GenerateSelfSignedCert()
if err != nil {
log.Warning(err)
}
ls.defaultCert = &defaultCert
s.BindFunc("", ls)
s.SearchFunc("", ls)
return ls

View File

@ -2,8 +2,13 @@ package ldap
import (
"fmt"
"math/big"
"reflect"
"strconv"
"strings"
"github.com/nmcclain/ldap"
log "github.com/sirupsen/logrus"
"goauthentik.io/api"
)
@ -14,6 +19,24 @@ func BoolToString(in bool) string {
return "false"
}
func ldapResolveTypeSingle(in interface{}) *string {
switch t := in.(type) {
case string:
return &t
case *string:
return t
case bool:
s := BoolToString(t)
return &s
case *bool:
s := BoolToString(*t)
return &s
default:
log.WithField("type", reflect.TypeOf(in).String()).Warning("Type can't be mapped to LDAP yet")
return nil
}
}
func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
attrList := []*ldap.EntryAttribute{}
a := attrs.(*map[string]interface{})
@ -22,10 +45,19 @@ func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
switch t := attrValue.(type) {
case []string:
entry.Values = t
case string:
entry.Values = []string{t}
case bool:
entry.Values = []string{BoolToString(t)}
case *[]string:
entry.Values = *t
case []interface{}:
entry.Values = make([]string, len(t))
for idx, v := range t {
v := ldapResolveTypeSingle(v)
entry.Values[idx] = *v
}
default:
v := ldapResolveTypeSingle(t)
if v != nil {
entry.Values = []string{*v}
}
}
attrList = append(attrList, entry)
}
@ -35,11 +67,72 @@ func AKAttrsToLDAP(attrs interface{}) []*ldap.EntryAttribute {
func (pi *ProviderInstance) GroupsForUser(user api.User) []string {
groups := make([]string, len(user.Groups))
for i, group := range user.Groups {
groups[i] = pi.GetGroupDN(group)
groups[i] = pi.GetGroupDN(group.Name)
}
return groups
}
func (pi *ProviderInstance) GetGroupDN(group api.Group) string {
return fmt.Sprintf("cn=%s,%s", group.Name, pi.GroupDN)
func (pi *ProviderInstance) UsersForGroup(group api.Group) []string {
users := make([]string, len(group.UsersObj))
for i, user := range group.UsersObj {
users[i] = pi.GetUserDN(user.Username)
}
return users
}
func (pi *ProviderInstance) APIGroupToLDAPGroup(g api.Group) LDAPGroup {
return LDAPGroup{
dn: pi.GetGroupDN(g.Name),
cn: g.Name,
uid: string(g.Pk),
gidNumber: pi.GetGidNumber(g),
member: pi.UsersForGroup(g),
isVirtualGroup: false,
isSuperuser: *g.IsSuperuser,
akAttributes: g.Attributes,
}
}
func (pi *ProviderInstance) APIUserToLDAPGroup(u api.User) LDAPGroup {
return LDAPGroup{
dn: pi.GetGroupDN(u.Username),
cn: u.Username,
uid: u.Uid,
gidNumber: pi.GetUidNumber(u),
member: []string{pi.GetUserDN(u.Username)},
isVirtualGroup: true,
isSuperuser: false,
akAttributes: nil,
}
}
func (pi *ProviderInstance) GetUserDN(user string) string {
return fmt.Sprintf("cn=%s,%s", user, pi.UserDN)
}
func (pi *ProviderInstance) GetGroupDN(group string) string {
return fmt.Sprintf("cn=%s,%s", group, pi.GroupDN)
}
func (pi *ProviderInstance) GetUidNumber(user api.User) string {
return strconv.FormatInt(int64(pi.uidStartNumber+user.Pk), 10)
}
func (pi *ProviderInstance) GetGidNumber(group api.Group) string {
return strconv.FormatInt(int64(pi.gidStartNumber+pi.GetRIDForGroup(group.Pk)), 10)
}
func (pi *ProviderInstance) GetRIDForGroup(uid string) int32 {
var i big.Int
i.SetString(strings.Replace(uid, "-", "", -1), 16)
intStr := i.String()
// Get the last 5 characters/digits of the int-version of the UUID
gid, err := strconv.Atoi(intStr[len(intStr)-5:])
if err != nil {
panic(err)
}
return int32(gid)
}

View File

@ -1,7 +1,6 @@
package proxy
import (
"context"
"crypto/tls"
"net"
"net/http"
@ -16,6 +15,7 @@ import (
"github.com/oauth2-proxy/oauth2-proxy/pkg/validation"
log "github.com/sirupsen/logrus"
"goauthentik.io/api"
"goauthentik.io/internal/outpost/ak"
)
type providerBundle struct {
@ -90,23 +90,12 @@ func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.
if provider.Certificate.Get() != nil {
pb.log.WithField("provider", provider.Name).Debug("Enabling TLS")
cert, _, err := pb.s.ak.Client.CryptoApi.CryptoCertificatekeypairsViewCertificateRetrieve(context.Background(), *provider.Certificate.Get()).Execute()
cert, err := ak.ParseCertificate(*provider.Certificate.Get(), pb.s.ak.Client.CryptoApi)
if err != nil {
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch certificate")
return providerOpts
}
key, _, err := pb.s.ak.Client.CryptoApi.CryptoCertificatekeypairsViewPrivateKeyRetrieve(context.Background(), *provider.Certificate.Get()).Execute()
if err != nil {
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch private key")
return providerOpts
}
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
if err != nil {
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to parse certificate")
return providerOpts
}
pb.cert = &x509cert
pb.cert = cert
pb.log.WithField("provider", provider.Name).Debug("Loaded certificates")
}
return providerOpts

View File

@ -6,6 +6,7 @@ import (
"goauthentik.io/internal/config"
"goauthentik.io/internal/constants"
staticWeb "goauthentik.io/web"
staticDocs "goauthentik.io/website"
)
func (ws *WebServer) configureStatic() {
@ -16,14 +17,15 @@ func (ws *WebServer) configureStatic() {
ws.log.Debug("Using local static files")
statRouter.PathPrefix("/static/dist").Handler(http.StripPrefix("/static/dist", http.FileServer(http.Dir("./web/dist"))))
statRouter.PathPrefix("/static/authentik").Handler(http.StripPrefix("/static/authentik", http.FileServer(http.Dir("./web/authentik"))))
statRouter.PathPrefix("/media").Handler(http.StripPrefix("/media", fs))
statRouter.PathPrefix("/help").Handler(http.StripPrefix("/help", http.FileServer(http.Dir("./website/help"))))
} else {
statRouter.Use(ws.staticHeaderMiddleware)
ws.log.Debug("Using packaged static files with aggressive caching")
statRouter.PathPrefix("/static/dist").Handler(http.StripPrefix("/static", http.FileServer(http.FS(staticWeb.StaticDist))))
statRouter.PathPrefix("/static/authentik").Handler(http.StripPrefix("/static", http.FileServer(http.FS(staticWeb.StaticAuthentik))))
statRouter.PathPrefix("/media").Handler(http.StripPrefix("/media", fs))
statRouter.PathPrefix("/help").Handler(http.FileServer(http.FS(staticDocs.Help)))
}
statRouter.PathPrefix("/media").Handler(http.StripPrefix("/media", fs))
ws.lh.Path("/robots.txt").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header()["Content-Type"] = []string{"text/plain"}
rw.WriteHeader(200)

View File

@ -14,7 +14,7 @@ RUN docker-entrypoint.sh generate \
rm -f /local/api/go.mod /local/api/go.sum
# Stage 2: Build
FROM golang:1.16.5 AS builder
FROM golang:1.16.6 AS builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH

View File

@ -40,6 +40,7 @@ if __name__ == "__main__":
user=CONFIG.y("postgresql.user"),
password=CONFIG.y("postgresql.password"),
host=CONFIG.y("postgresql.host"),
port=int(CONFIG.y("postgresql.port")),
)
curr = conn.cursor()
# lock an advisory lock to prevent multiple instances from migrating at once

View File

@ -40,11 +40,15 @@ while True:
sleep(1)
j_print(f"PostgreSQL Connection failed, retrying... ({exc})")
REDIS_PROTOCOL_PREFIX = "redis://"
if CONFIG.y_bool("redis.tls", False):
REDIS_PROTOCOL_PREFIX = "rediss://"
while True:
try:
redis = Redis.from_url(
f"redis://:{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:6379"
f"/{CONFIG.y('redis.ws_db')}"
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.ws_db')}"
)
redis.ping()
break

View File

@ -14,7 +14,7 @@ RUN docker-entrypoint.sh generate \
rm -f /local/api/go.mod /local/api/go.sum
# Stage 2: Build
FROM golang:1.16.5 AS builder
FROM golang:1.16.6 AS builder
ARG GIT_BUILD_HASH
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH

View File

@ -2429,6 +2429,15 @@ paths:
name: description
schema:
type: string
- in: query
name: expires
schema:
type: string
format: date-time
- in: query
name: expiring
schema:
type: boolean
- in: query
name: identifier
schema:
@ -19432,6 +19441,7 @@ components:
- suspicious_request
- password_set
- secret_view
- secret_rotate
- invitation_used
- authorize_application
- source_linked
@ -19927,11 +19937,100 @@ components:
attributes:
type: object
additionalProperties: {}
users_obj:
type: array
items:
$ref: '#/components/schemas/GroupMember'
readOnly: true
required:
- name
- parent
- pk
- users
- users_obj
GroupMember:
type: object
description: Stripped down user serializer to show relevant users for groups
properties:
pk:
type: integer
readOnly: true
title: ID
username:
type: string
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
only.
pattern: ^[\w.@+-]+$
maxLength: 150
name:
type: string
description: User's display name.
is_active:
type: boolean
title: Active
description: Designates whether this user should be treated as active. Unselect
this instead of deleting accounts.
last_login:
type: string
format: date-time
nullable: true
is_superuser:
type: boolean
readOnly: true
email:
type: string
format: email
title: Email address
maxLength: 254
avatar:
type: string
readOnly: true
attributes:
type: object
additionalProperties: {}
uid:
type: string
readOnly: true
required:
- avatar
- is_superuser
- name
- pk
- uid
- username
GroupMemberRequest:
type: object
description: Stripped down user serializer to show relevant users for groups
properties:
username:
type: string
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
only.
pattern: ^[\w.@+-]+$
maxLength: 150
name:
type: string
description: User's display name.
is_active:
type: boolean
title: Active
description: Designates whether this user should be treated as active. Unselect
this instead of deleting accounts.
last_login:
type: string
format: date-time
nullable: true
email:
type: string
format: email
title: Email address
maxLength: 254
attributes:
type: object
additionalProperties: {}
required:
- name
- username
GroupRequest:
type: object
description: Group Serializer
@ -20402,6 +20501,27 @@ components:
nullable: true
description: Users in this group can do search queries. If not set, every
user can execute search queries.
certificate:
type: string
format: uuid
nullable: true
tls_server_name:
type: string
uid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for uidNumbers, this number is added to the user.Pk
to make sure that the numbers aren't too low for POSIX users. Default
is 2000 to ensure that we don't collide with local users uidNumber
gid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for gidNumbers, this number is added to a number
generated from the group.Pk to make sure that the numbers aren't too low
for POSIX groups. Default is 4000 to ensure that we don't collide with
local groups or users primary groups gidNumber
required:
- application_slug
- bind_flow_slug
@ -20514,6 +20634,27 @@ components:
nullable: true
description: Users in this group can do search queries. If not set, every
user can execute search queries.
certificate:
type: string
format: uuid
nullable: true
tls_server_name:
type: string
uid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for uidNumbers, this number is added to the user.Pk
to make sure that the numbers aren't too low for POSIX users. Default
is 2000 to ensure that we don't collide with local users uidNumber
gid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for gidNumbers, this number is added to a number
generated from the group.Pk to make sure that the numbers aren't too low
for POSIX groups. Default is 4000 to ensure that we don't collide with
local groups or users primary groups gidNumber
required:
- assigned_application_name
- assigned_application_slug
@ -20547,6 +20688,27 @@ components:
nullable: true
description: Users in this group can do search queries. If not set, every
user can execute search queries.
certificate:
type: string
format: uuid
nullable: true
tls_server_name:
type: string
uid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for uidNumbers, this number is added to the user.Pk
to make sure that the numbers aren't too low for POSIX users. Default
is 2000 to ensure that we don't collide with local users uidNumber
gid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for gidNumbers, this number is added to a number
generated from the group.Pk to make sure that the numbers aren't too low
for POSIX groups. Default is 4000 to ensure that we don't collide with
local groups or users primary groups gidNumber
required:
- authorization_flow
- name
@ -24899,6 +25061,27 @@ components:
nullable: true
description: Users in this group can do search queries. If not set, every
user can execute search queries.
certificate:
type: string
format: uuid
nullable: true
tls_server_name:
type: string
uid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for uidNumbers, this number is added to the user.Pk
to make sure that the numbers aren't too low for POSIX users. Default
is 2000 to ensure that we don't collide with local users uidNumber
gid_start_number:
type: integer
maximum: 2147483647
minimum: -2147483648
description: The start for gidNumbers, this number is added to a number
generated from the group.Pk to make sure that the numbers aren't too low
for POSIX groups. Default is 4000 to ensure that we don't collide with
local groups or users primary groups gidNumber
PatchedLDAPSourceRequest:
type: object
description: LDAP Source Serializer

View File

@ -194,6 +194,8 @@ class TestProviderLDAP(SeleniumTestCase):
"organizationalPerson",
"goauthentik.io/ldap/user",
],
"uidNumber": [str(2000 + outpost_user.pk)],
"gidNumber": [str(2000 + outpost_user.pk)],
"memberOf": [],
"accountStatus": ["true"],
"superuser": ["false"],
@ -217,6 +219,8 @@ class TestProviderLDAP(SeleniumTestCase):
"organizationalPerson",
"goauthentik.io/ldap/user",
],
"uidNumber": [str(2000 + USER().pk)],
"gidNumber": [str(2000 + USER().pk)],
"memberOf": [
"cn=authentik Admins,ou=groups,dc=ldap,dc=goauthentik,dc=io"
],

349
web/package-lock.json generated
View File

@ -23,14 +23,14 @@
"@polymer/paper-input": "^3.2.1",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.8.0",
"@sentry/tracing": "^6.8.0",
"@rollup/plugin-typescript": "^8.2.3",
"@sentry/browser": "^6.9.0",
"@sentry/tracing": "^6.9.0",
"@types/chart.js": "^2.9.34",
"@types/codemirror": "5.60.2",
"@types/grecaptcha": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0",
@ -48,7 +48,7 @@
"lit-html": "^1.4.1",
"moment": "^2.29.1",
"rapidoc": "^9.0.0",
"rollup": "^2.52.8",
"rollup": "^2.53.2",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",
@ -66,16 +66,15 @@
},
"api": {
"name": "authentik-api",
"version": "1.0.0",
"devDependencies": {
"typescript": "^3.6"
"version": "0.0.1",
"dependencies": {
"typescript": "^3.9.5"
}
},
"api/node_modules/typescript": {
"version": "3.9.9",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -2299,15 +2298,20 @@
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz",
"integrity": "sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.3.tgz",
"integrity": "sha512-bSkd+DD3wP9OLU6lPet59B6jJF29/RlxHkbwvOVOcf1nk8eQYWw24HpldEdrPo/WG0QAPD7TqI7+2y5jtdqjww==",
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
"resolve": "^1.17.0"
},
"engines": {
"node": ">=8.0.0"
},
"peerDependencies": {
"rollup": "^2.14.0",
"tslib": "*",
"typescript": ">=3.7.0"
}
},
"node_modules/@rollup/pluginutils": {
@ -2334,13 +2338,13 @@
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
},
"node_modules/@sentry/browser": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.8.0.tgz",
"integrity": "sha512-nxa71csHlG5sMHUxI4e4xxuCWtbCv/QbBfMsYw7ncJSfCKG3yNlCVh8NJ7NS0rZW/MJUT6S6+r93zw0HetNDOA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
"dependencies": {
"@sentry/core": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/core": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2353,14 +2357,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/core": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.8.0.tgz",
"integrity": "sha512-vJzWt/znEB+JqVwtwfjkRrAYRN+ep+l070Ti8GhJnvwU4IDtVlV3T/jVNrj6rl6UChcczaJQMxVxtG5x0crlAA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
"dependencies": {
"@sentry/hub": "6.8.0",
"@sentry/minimal": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/minimal": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2373,12 +2377,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/hub": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.8.0.tgz",
"integrity": "sha512-hFrI2Ss1fTov7CH64FJpigqRxH7YvSnGeqxT9Jc1BL7nzW/vgCK+Oh2mOZbosTcrzoDv+lE8ViOnSN3w/fo+rg==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
"dependencies": {
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2391,12 +2395,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/minimal": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.8.0.tgz",
"integrity": "sha512-MRxUKXiiYwKjp8mOQMpTpEuIby1Jh3zRTU0cmGZtfsZ38BC1JOle8xlwC4FdtOH+VvjSYnPBMya5lgNHNPUJDQ==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
"dependencies": {
"@sentry/hub": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/types": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2409,14 +2413,14 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/tracing": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.8.0.tgz",
"integrity": "sha512-3gDkQnmOuOjHz5rY7BOatLEUksANU3efR8wuBa2ujsPQvoLSLFuyZpRjPPsxuUHQOqAYIbSNAoDloXECvQeHjw==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
"dependencies": {
"@sentry/hub": "6.8.0",
"@sentry/minimal": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/minimal": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2429,19 +2433,19 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@sentry/types": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.8.0.tgz",
"integrity": "sha512-PbSxqlh6Fd5thNU5f8EVYBVvX+G7XdPA+ThNb2QvSK8yv3rIf0McHTyF6sIebgJ38OYN7ZFK7vvhC/RgSAfYTA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA==",
"engines": {
"node": ">=6"
}
},
"node_modules/@sentry/utils": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.8.0.tgz",
"integrity": "sha512-OYlI2JNrcWKMdvYbWNdQwR4QBVv2V0y5wK0U6f53nArv6RsyO5TzwRu5rMVSIZofUUqjoE5hl27jqnR+vpUrsA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
"dependencies": {
"@sentry/types": "6.8.0",
"@sentry/types": "6.9.0",
"tslib": "^1.9.3"
},
"engines": {
@ -2537,9 +2541,9 @@
}
},
"node_modules/@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
"integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg=="
},
"node_modules/@types/minimatch": {
"version": "3.0.3",
@ -2599,12 +2603,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz",
"integrity": "sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz",
"integrity": "sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg==",
"dependencies": {
"@typescript-eslint/experimental-utils": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/experimental-utils": "4.28.3",
"@typescript-eslint/scope-manager": "4.28.3",
"debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.1.0",
@ -2629,14 +2633,14 @@
}
},
"node_modules/@typescript-eslint/experimental-utils": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz",
"integrity": "sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz",
"integrity": "sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw==",
"dependencies": {
"@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/typescript-estree": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.3",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/typescript-estree": "4.28.3",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
@ -2669,13 +2673,13 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.2.tgz",
"integrity": "sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.3.tgz",
"integrity": "sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ==",
"dependencies": {
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/typescript-estree": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.3",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/typescript-estree": "4.28.3",
"debug": "^4.3.1"
},
"engines": {
@ -2695,12 +2699,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz",
"integrity": "sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz",
"integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==",
"dependencies": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/visitor-keys": "4.28.2"
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/visitor-keys": "4.28.3"
},
"engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
@ -2711,9 +2715,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.2.tgz",
"integrity": "sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz",
"integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA==",
"engines": {
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
},
@ -2723,12 +2727,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz",
"integrity": "sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz",
"integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==",
"dependencies": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/visitor-keys": "4.28.2",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/visitor-keys": "4.28.3",
"debug": "^4.3.1",
"globby": "^11.0.3",
"is-glob": "^4.0.1",
@ -2768,11 +2772,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz",
"integrity": "sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz",
"integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==",
"dependencies": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/types": "4.28.3",
"eslint-visitor-keys": "^2.0.0"
},
"engines": {
@ -6791,9 +6795,9 @@
}
},
"node_modules/rollup": {
"version": "2.52.8",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.8.tgz",
"integrity": "sha512-IjAB0C6KK5/lvqzJWAzsvOik+jV5Bt907QdkQ/gDP4j+R9KYNI1tjqdxiPitGPVrWC21Mf/ucXgowUjN/VemaQ==",
"version": "2.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz",
"integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==",
"bin": {
"rollup": "dist/bin/rollup"
},
@ -9675,9 +9679,9 @@
}
},
"@rollup/plugin-typescript": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz",
"integrity": "sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==",
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.3.tgz",
"integrity": "sha512-bSkd+DD3wP9OLU6lPet59B6jJF29/RlxHkbwvOVOcf1nk8eQYWw24HpldEdrPo/WG0QAPD7TqI7+2y5jtdqjww==",
"requires": {
"@rollup/pluginutils": "^3.1.0",
"resolve": "^1.17.0"
@ -9706,13 +9710,13 @@
}
},
"@sentry/browser": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.8.0.tgz",
"integrity": "sha512-nxa71csHlG5sMHUxI4e4xxuCWtbCv/QbBfMsYw7ncJSfCKG3yNlCVh8NJ7NS0rZW/MJUT6S6+r93zw0HetNDOA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
"requires": {
"@sentry/core": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/core": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9724,14 +9728,14 @@
}
},
"@sentry/core": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.8.0.tgz",
"integrity": "sha512-vJzWt/znEB+JqVwtwfjkRrAYRN+ep+l070Ti8GhJnvwU4IDtVlV3T/jVNrj6rl6UChcczaJQMxVxtG5x0crlAA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
"requires": {
"@sentry/hub": "6.8.0",
"@sentry/minimal": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/minimal": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9743,12 +9747,12 @@
}
},
"@sentry/hub": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.8.0.tgz",
"integrity": "sha512-hFrI2Ss1fTov7CH64FJpigqRxH7YvSnGeqxT9Jc1BL7nzW/vgCK+Oh2mOZbosTcrzoDv+lE8ViOnSN3w/fo+rg==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
"requires": {
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9760,12 +9764,12 @@
}
},
"@sentry/minimal": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.8.0.tgz",
"integrity": "sha512-MRxUKXiiYwKjp8mOQMpTpEuIby1Jh3zRTU0cmGZtfsZ38BC1JOle8xlwC4FdtOH+VvjSYnPBMya5lgNHNPUJDQ==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
"requires": {
"@sentry/hub": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/types": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9777,14 +9781,14 @@
}
},
"@sentry/tracing": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.8.0.tgz",
"integrity": "sha512-3gDkQnmOuOjHz5rY7BOatLEUksANU3efR8wuBa2ujsPQvoLSLFuyZpRjPPsxuUHQOqAYIbSNAoDloXECvQeHjw==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
"requires": {
"@sentry/hub": "6.8.0",
"@sentry/minimal": "6.8.0",
"@sentry/types": "6.8.0",
"@sentry/utils": "6.8.0",
"@sentry/hub": "6.9.0",
"@sentry/minimal": "6.9.0",
"@sentry/types": "6.9.0",
"@sentry/utils": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9796,16 +9800,16 @@
}
},
"@sentry/types": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.8.0.tgz",
"integrity": "sha512-PbSxqlh6Fd5thNU5f8EVYBVvX+G7XdPA+ThNb2QvSK8yv3rIf0McHTyF6sIebgJ38OYN7ZFK7vvhC/RgSAfYTA=="
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA=="
},
"@sentry/utils": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.8.0.tgz",
"integrity": "sha512-OYlI2JNrcWKMdvYbWNdQwR4QBVv2V0y5wK0U6f53nArv6RsyO5TzwRu5rMVSIZofUUqjoE5hl27jqnR+vpUrsA==",
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
"requires": {
"@sentry/types": "6.8.0",
"@sentry/types": "6.9.0",
"tslib": "^1.9.3"
},
"dependencies": {
@ -9900,9 +9904,9 @@
}
},
"@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz",
"integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg=="
},
"@types/minimatch": {
"version": "3.0.3",
@ -9962,12 +9966,12 @@
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
},
"@typescript-eslint/eslint-plugin": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz",
"integrity": "sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz",
"integrity": "sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg==",
"requires": {
"@typescript-eslint/experimental-utils": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/experimental-utils": "4.28.3",
"@typescript-eslint/scope-manager": "4.28.3",
"debug": "^4.3.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.1.0",
@ -9976,14 +9980,14 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz",
"integrity": "sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz",
"integrity": "sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw==",
"requires": {
"@types/json-schema": "^7.0.7",
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/typescript-estree": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.3",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/typescript-estree": "4.28.3",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
@ -9999,37 +10003,37 @@
}
},
"@typescript-eslint/parser": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.2.tgz",
"integrity": "sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.3.tgz",
"integrity": "sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ==",
"requires": {
"@typescript-eslint/scope-manager": "4.28.2",
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/typescript-estree": "4.28.2",
"@typescript-eslint/scope-manager": "4.28.3",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/typescript-estree": "4.28.3",
"debug": "^4.3.1"
}
},
"@typescript-eslint/scope-manager": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz",
"integrity": "sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz",
"integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==",
"requires": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/visitor-keys": "4.28.2"
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/visitor-keys": "4.28.3"
}
},
"@typescript-eslint/types": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.2.tgz",
"integrity": "sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA=="
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz",
"integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA=="
},
"@typescript-eslint/typescript-estree": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz",
"integrity": "sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz",
"integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==",
"requires": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/visitor-keys": "4.28.2",
"@typescript-eslint/types": "4.28.3",
"@typescript-eslint/visitor-keys": "4.28.3",
"debug": "^4.3.1",
"globby": "^11.0.3",
"is-glob": "^4.0.1",
@ -10053,11 +10057,11 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz",
"integrity": "sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==",
"version": "4.28.3",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz",
"integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==",
"requires": {
"@typescript-eslint/types": "4.28.2",
"@typescript-eslint/types": "4.28.3",
"eslint-visitor-keys": "^2.0.0"
}
},
@ -10201,14 +10205,13 @@
"authentik-api": {
"version": "file:api",
"requires": {
"typescript": "^3.6"
"typescript": "^3.9.5"
},
"dependencies": {
"typescript": {
"version": "3.9.9",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
"dev": true
"integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w=="
}
}
},
@ -13239,9 +13242,9 @@
}
},
"rollup": {
"version": "2.52.8",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.8.tgz",
"integrity": "sha512-IjAB0C6KK5/lvqzJWAzsvOik+jV5Bt907QdkQ/gDP4j+R9KYNI1tjqdxiPitGPVrWC21Mf/ucXgowUjN/VemaQ==",
"version": "2.53.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz",
"integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==",
"requires": {
"fsevents": "~2.3.2"
}

View File

@ -52,14 +52,14 @@
"@polymer/paper-input": "^3.2.1",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@sentry/browser": "^6.8.0",
"@sentry/tracing": "^6.8.0",
"@rollup/plugin-typescript": "^8.2.3",
"@sentry/browser": "^6.9.0",
"@sentry/tracing": "^6.9.0",
"@types/chart.js": "^2.9.34",
"@types/codemirror": "5.60.2",
"@types/grecaptcha": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"@webcomponents/webcomponentsjs": "^2.5.0",
"authentik-api": "file:api",
"babel-plugin-macros": "^3.1.0",
@ -77,7 +77,7 @@
"lit-html": "^1.4.1",
"moment": "^2.29.1",
"rapidoc": "^9.0.0",
"rollup": "^2.52.8",
"rollup": "^2.53.2",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-cssimport": "^1.0.2",

View File

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

View File

@ -483,6 +483,7 @@ msgid "Case insensitive matching"
msgstr "Case insensitive matching"
#: src/pages/crypto/CertificateKeyPairForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts
msgid "Certificate"
msgstr "Certificate"
@ -1669,6 +1670,10 @@ msgstr "From"
msgid "From address"
msgstr "From address"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "GID start number"
msgstr "GID start number"
#: src/pages/admin-overview/AdminOverviewPage.ts
msgid "General system status"
msgstr "General system status"
@ -2079,6 +2084,7 @@ msgstr "Loading"
#: src/pages/property-mappings/PropertyMappingTestForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
@ -3222,6 +3228,10 @@ msgstr "Separator: Static Separator Line"
msgid "Server URI"
msgstr "Server URI"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Server name for which this provider's certificate is valid for."
msgstr "Server name for which this provider's certificate is valid for."
#: src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts
msgid "Server validation of credential failed: {err}"
msgstr "Server validation of credential failed: {err}"
@ -3791,6 +3801,10 @@ msgstr "System Tasks"
msgid "TLS Authentication Certificate"
msgstr "TLS Authentication Certificate"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "TLS Server name"
msgstr "TLS Server name"
#: src/pages/outposts/ServiceConnectionDockerForm.ts
msgid "TLS Verification Certificate"
msgstr "TLS Verification Certificate"
@ -3877,6 +3891,14 @@ msgstr ""
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
msgstr "The policy takes a random time to execute. This controls the minimum time it will take."
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber"
msgstr "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber"
msgstr "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber"
#: src/pages/flows/BoundStagesList.ts
msgid "These bindings control if this stage will be applied to the flow."
msgstr "These bindings control if this stage will be applied to the flow."
@ -4035,6 +4057,10 @@ msgstr "UI settings"
msgid "UID"
msgstr "UID"
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "UID start number"
msgstr "UID start number"
#: src/flows/stages/identification/IdentificationStage.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
msgid "UPN"

View File

@ -479,6 +479,7 @@ msgid "Case insensitive matching"
msgstr ""
#: src/pages/crypto/CertificateKeyPairForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts
msgid "Certificate"
msgstr ""
@ -1661,6 +1662,10 @@ msgstr ""
msgid "From address"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "GID start number"
msgstr ""
#: src/pages/admin-overview/AdminOverviewPage.ts
msgid "General system status"
msgstr ""
@ -2071,6 +2076,7 @@ msgstr ""
#: src/pages/property-mappings/PropertyMappingTestForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/ldap/LDAPProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
@ -3214,6 +3220,10 @@ msgstr ""
msgid "Server URI"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "Server name for which this provider's certificate is valid for."
msgstr ""
#: src/flows/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts
msgid "Server validation of credential failed: {err}"
msgstr ""
@ -3783,6 +3793,10 @@ msgstr ""
msgid "TLS Authentication Certificate"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "TLS Server name"
msgstr ""
#: src/pages/outposts/ServiceConnectionDockerForm.ts
msgid "TLS Verification Certificate"
msgstr ""
@ -3866,6 +3880,14 @@ msgstr ""
msgid "The policy takes a random time to execute. This controls the minimum time it will take."
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber"
msgstr ""
#: src/pages/flows/BoundStagesList.ts
msgid "These bindings control if this stage will be applied to the flow."
msgstr ""
@ -4020,6 +4042,10 @@ msgstr ""
msgid "UID"
msgstr ""
#: src/pages/providers/ldap/LDAPProviderForm.ts
msgid "UID start number"
msgstr ""
#: src/flows/stages/identification/IdentificationStage.ts
#: src/pages/stages/identification/IdentificationStageForm.ts
msgid "UPN"

View File

@ -1,4 +1,4 @@
import { FlowsApi, ProvidersApi, LDAPProvider, CoreApi, FlowsInstancesListDesignationEnum } from "authentik-api";
import { FlowsApi, ProvidersApi, LDAPProvider, CoreApi, FlowsInstancesListDesignationEnum, CryptoApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement } from "lit-element";
import { html, TemplateResult } from "lit-html";
@ -90,6 +90,41 @@ export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
<input type="text" value="${first(this.instance?.baseDn, "DC=ldap,DC=goauthentik,DC=io")}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`LDAP DN under which bind requests and search requests can be made.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`TLS Server name`}
name="tlsServerName">
<input type="text" value="${first(this.instance?.tlsServerName, "")}" class="pf-c-form-control">
<p class="pf-c-form__helper-text">${t`Server name for which this provider's certificate is valid for.`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Certificate`}
name="certificate">
<select class="pf-c-form-control">
<option value="" ?selected=${this.instance?.certificate === undefined}>---------</option>
${until(new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList({
ordering: "pk",
hasKey: true,
}).then(keys => {
return keys.results.map(key => {
return html`<option value=${ifDefined(key.pk)} ?selected=${this.instance?.certificate === key.pk}>${key.name}</option>`;
});
}), html`<option>${t`Loading...`}</option>`)}
</select>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`UID start number`}
?required=${true}
name="uidStartNumber">
<input type="number" value="${first(this.instance?.uidStartNumber, 2000)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`The start for uidNumbers, this number is added to the user.Pk to make sure that the numbers aren't too low for POSIX users. Default is 2000 to ensure that we don't collide with local users uidNumber`}</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`GID start number`}
?required=${true}
name="gidStartNumber">
<input type="number" value="${first(this.instance?.gidStartNumber, 4000)}" class="pf-c-form-control" required>
<p class="pf-c-form__helper-text">${t`The start for gidNumbers, this number is added to a number generated from the group.Pk to make sure that the numbers aren't too low for POSIX groups. Default is 4000 to ensure that we don't collide with local groups or users primary groups gidNumber`}</p>
</ak-form-element-horizontal>
</div>
</ak-form-group>
</form>`;

View File

@ -47,9 +47,8 @@ export class UserTokenForm extends ModelForm<Token, string> {
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Description`}
?required=${true}
name="description">
<input type="text" value="${ifDefined(this.instance?.description)}" class="pf-c-form-control" required>
<input type="text" value="${ifDefined(this.instance?.description)}" class="pf-c-form-control">
</ak-form-element-horizontal>
</form>`;
}

1
website/.gitignore vendored
View File

@ -3,6 +3,7 @@
# Production
/build
/help
# Generated files
.docusaurus

View File

@ -5,7 +5,7 @@ title: Frontend-only development environment
If you want to only make changes on the UI, you don't need a backend running from source. You can user the docker-compose install with a few customizations.
1. Clone the git repo from https://github.com/goauthentik/authentik
2. In the cloned repository, follow the docker-compose installation instructions [here](../../docs/installation/docker-compose)
2. In the cloned repository, follow the docker-compose installation instructions [here](/docs/installation/docker-compose)
3. Add the following entry to your `.env` file:
```

View File

@ -22,7 +22,7 @@ You can bind using the DN `cn=<username>,ou=users,<base DN>`, or using the follo
ldapsearch \
-x \ # Only simple binds are currently supported
-h *ip* \
-p 3389 \
-p 389 \
-D 'cn=*user*,ou=users,DC=ldap,DC=goauthentik,DC=io' \ # Bind user and password
-w '*password*' \
-b 'ou=users,DC=ldap,DC=goauthentik,DC=io' \ # The search base
@ -33,6 +33,7 @@ The following fields are currently sent for users:
- `cn`: User's username
- `uid`: Unique user identifier
- `uidNumber`: A unique numeric identifier for the user
- `name`: User's name
- `displayName`: User's name
- `mail`: User's email address
@ -48,8 +49,19 @@ The following fields are current set for groups:
- `cn`: The group's name
- `uid`: Unique group identifier
- `gidNumber`: A unique numeric identifier for the group
- `member`: A list of all DNs of the groups members
- `objectClass`: A list of these strings:
- "group"
- "goauthentik.io/ldap/group"
**Additionally**, for both users and groups, any attributes you set are also present as LDAP Attributes.
A virtual group is also created for each user, they have the same fields as groups but have an additional objectClass: `goauthentik.io/ldap/virtual-group`.
The virtual groups gidNumber is equal to the uidNumber of the user.
**Additionally**, for both users and (non-virtual) groups, any attributes you set are also present as LDAP Attributes.
## SSL
You can also configure SSL for your LDAP Providers by selecting a certificate and a server name in the provider settings.
This enables you to bind on port 636 using LDAPS, StartTLS is not supported.

View File

@ -0,0 +1,42 @@
---
title: Release 2021.7
slug: "2021.7"
---
## Headline Changes
- TLS Support for LDAP Providers
You can now configure certificates for your LDAP Providers, meaning that all communication will be done encrypted.
- Add bundeled docs
You can now browse the authentik docs for your version by browsing to `/help`. This means you don't have to rely on an
internet connection to check the logs, and you also have the correct logs for your currently running version.
## Minor changes
- Fix LDAP outpost not parsing arrays from user and group attributes correctly
- Fix LDAP outpost missing a `member` field on groups with all member DNs
- Allow configuration of Redis port
- Tunnel Sentry requests through authentik to prevent them being blocked by ad-blockers
- core: fix error when setting icon/background to url longer than 100 chars
- events: fix error when slack notification request failed without a response
- flows: allow variable substitution in flow titles
- providers/oauth2: allow blank redirect_uris to allow any redirect_uri
- providers/saml: fix parsing of POST bindings
- root: set samesite to None for SAML POST flows
- root: subclass SessionMiddleware to set Secure and SameSite flag depending on context
- web: fix error when showing error message of request
## Upgrading
This release does not introduce any new requirements.
### docker-compose
Download the docker-compose file for 2021.7 from [here](https://raw.githubusercontent.com/goauthentik/authentik/version-2021.7/docker-compose.yml). Afterwards, simply run `docker-compose up -d`.
### Kubernetes
Upgrade to the latest chart version to get the new images.

View File

@ -0,0 +1,86 @@
const mainConfig = require("./docusaurus.config");
module.exports = {
title: "authentik",
tagline: "Making authentication simple.",
url: "https://goauthentik.io",
baseUrl: "/help/",
onBrokenLinks: "throw",
favicon: "img/icon.png",
organizationName: "BeryJu",
projectName: "authentik",
themeConfig: {
navbar: {
title: "authentik",
logo: {
alt: "authentik logo",
src: "img/icon_left_brand.svg",
},
items: [
{
to: "docs/",
activeBasePath: "docs",
label: "Docs",
position: "left",
},
{
to: "developer-docs/",
activeBasePath: "developer-docs",
label: "Developer Docs",
position: "left",
},
{
href: "https://github.com/goauthentik/authentik",
label: "GitHub",
position: "right",
},
{
href: "https://discord.gg/jg33eMhnj6",
label: "Discord",
position: "right",
},
],
},
footer: mainConfig.themeConfig.footer,
colorMode: mainConfig.themeConfig.colorMode,
},
presets: [
[
"@docusaurus/preset-classic",
{
docs: {
id: "docs",
sidebarPath: require.resolve("./sidebars.js"),
editUrl: "https://github.com/goauthentik/authentik/edit/master/website/",
},
pages: false,
theme: {
customCss: require.resolve("./src/css/custom.css"),
},
},
],
],
plugins: [
[
'@docusaurus/plugin-content-docs',
{
id: 'docsDevelopers',
path: 'developer-docs',
routeBasePath: 'developer-docs',
sidebarPath: require.resolve('./sidebarsDev.js'),
editUrl: "https://github.com/goauthentik/authentik/edit/master/website/",
},
],
[
'@docusaurus/plugin-client-redirects',
{
redirects: [
{
to: '/docs/',
from: ['/'],
},
],
},
],
],
};

7104
website/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,18 +6,20 @@
"docusaurus": "docusaurus",
"watch": "docusaurus start",
"build": "docusaurus build",
"build-docs-only": "docusaurus build --config docusaurus.docs-only.js --out-dir help",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"serve": "docusaurus serve"
},
"dependencies": {
"@docusaurus/preset-classic": "2.0.0-beta.0",
"@docusaurus/plugin-client-redirects": "2.0.0-beta.3",
"@docusaurus/preset-classic": "2.0.0-beta.3",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"postcss": "^8.3.5",
"rapidoc": "^9.0.0",
"react": "^17.0.2",
"react-before-after-slider": "^1.0.4",
"react-before-after-slider-component": "^0.7.4",
"react-dom": "^17.0.2",
"react-feather": "^2.0.9",
"react-toggle": "^4.1.2"

View File

@ -37,6 +37,10 @@
}
.before-after-slider img {
max-width: none;
}
table {
display: table;
}

View File

@ -2,14 +2,17 @@ import React from "react";
import Layout from "@theme/Layout";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl";
import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly";
function APIBrowser() {
const context = useDocusaurusContext();
const { siteConfig = {} } = context;
import('rapidoc');
return (
<Layout title="API Browser" description={siteConfig.tagline}>
<rapi-doc
<BrowserOnly>
{() => {
import('rapidoc');
return <rapi-doc
spec-url={useBaseUrl("schema.yaml")}
allow-try="false"
show-header="false"
@ -19,6 +22,8 @@ function APIBrowser() {
allow-spec-url-load="false"
allow-spec-file-load="false">
</rapi-doc>
}}
</BrowserOnly>
</Layout>
);
}

View File

@ -6,8 +6,8 @@ import BrowserOnly from "@docusaurus/core/lib/client/exports/BrowserOnly";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl";
import styles from "./styles.module.css";
import BeforeAfterSlider from 'react-before-after-slider'
import Comparison from "../comparison";
import 'react-before-after-slider-component/dist/build.css';
const features = [
{
@ -105,12 +105,16 @@ function Home() {
<div>
<BrowserOnly>
{() => {
return <BeforeAfterSlider
className={styles.featureImage}
before={useBaseUrl("img/screen_apps_light.jpg")}
after={useBaseUrl("img/screen_apps_dark.jpg")}
width={640}
height={480}
const ReactBeforeSliderComponent = require('react-before-after-slider-component');
return <ReactBeforeSliderComponent
firstImage={{
id: 1,
imageUrl: useBaseUrl("img/screen_apps_light.jpg"),
}}
secondImage={{
id: 2,
imageUrl: useBaseUrl("img/screen_apps_dark.jpg"),
}}
/>
}}
</BrowserOnly>
@ -146,12 +150,16 @@ function Home() {
<div>
<BrowserOnly>
{() => {
return <BeforeAfterSlider
className={styles.featureImage}
before={useBaseUrl("img/screen_admin_light.jpg")}
after={useBaseUrl("img/screen_admin_dark.jpg")}
width={640}
height={480}
const ReactBeforeSliderComponent = require('react-before-after-slider-component');
return <ReactBeforeSliderComponent
firstImage={{
id: 1,
imageUrl: useBaseUrl("img/screen_admin_light.jpg"),
}}
secondImage={{
id: 2,
imageUrl: useBaseUrl("img/screen_admin_dark.jpg"),
}}
/>
}}
</BrowserOnly>

6
website/static.go Normal file
View File

@ -0,0 +1,6 @@
package web
import "embed"
//go:embed help/*
var Help embed.FS