Merge branch 'master' into version-2021.12
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> # Conflicts: # Dockerfile
This commit is contained in:
commit
5914bbf173
|
@ -114,16 +114,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Build web api client and web ui
|
||||
- name: Get static files from docker image
|
||||
run: |
|
||||
export NODE_ENV=production
|
||||
cd web
|
||||
npm i
|
||||
npm run build
|
||||
docker pull ghcr.io/goauthentik/server:latest
|
||||
container=$(docker container create ghcr.io/goauthentik/server:latest)
|
||||
docker cp ${container}:web/ .
|
||||
- name: Create a Sentry.io release
|
||||
uses: getsentry/action-release@v1
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
|
|
|
@ -28,7 +28,7 @@ ENV NODE_ENV=production
|
|||
RUN cd /work/web && npm i && npm run build
|
||||
|
||||
# Stage 4: Build go proxy
|
||||
FROM docker.io/golang:1.17.4-bullseye AS builder
|
||||
FROM docker.io/golang:1.17.5-bullseye AS builder
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
@ -64,8 +64,8 @@ RUN apt-get update && \
|
|||
apt-get clean && \
|
||||
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
|
||||
adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \
|
||||
mkdir /backups /certs && \
|
||||
chown authentik:authentik /backups /certs
|
||||
mkdir -p /backups /certs /media && \
|
||||
chown authentik:authentik /backups /certs /media
|
||||
|
||||
COPY ./authentik/ /authentik
|
||||
COPY ./pyproject.toml /
|
||||
|
|
6
Pipfile
6
Pipfile
|
@ -32,14 +32,15 @@ geoip2 = "*"
|
|||
gunicorn = "*"
|
||||
kubernetes = "==v19.15.0"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
# 4.6.5 and later remove `lxml-version.h` which is required by xmlsec
|
||||
lxml = "==4.6.4"
|
||||
packaging = "*"
|
||||
psycopg2-binary = "*"
|
||||
pycryptodome = "*"
|
||||
pyjwt = "*"
|
||||
pyyaml = "*"
|
||||
requests-oauthlib = "*"
|
||||
sentry-sdk = "*"
|
||||
sentry-sdk = { git = 'https://github.com/BeryJu/sentry-python.git', ref = 'bba7d80c05bc6845aa333ebbd87e3b76747ed355' }
|
||||
service_identity = "*"
|
||||
structlog = "*"
|
||||
swagger-spec-validator = "*"
|
||||
|
@ -50,6 +51,7 @@ uvicorn = {extras = ["standard"],version = "*"}
|
|||
webauthn = "*"
|
||||
xmlsec = "*"
|
||||
flower = "*"
|
||||
wsproto = "*"
|
||||
|
||||
[dev-packages]
|
||||
bandit = "*"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "6f7f70af39ff6e2d95dd62e225a52c5846957ebac5218aa2b49417fb7f9d8cf1"
|
||||
"sha256": "f826456d2aa4f1379f61f3c1f76ddd2db7af3395a272d6b56fd5402c4aa3ce2f"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -109,11 +109,11 @@
|
|||
},
|
||||
"amqp": {
|
||||
"hashes": [
|
||||
"sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2",
|
||||
"sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"
|
||||
"sha256:4d9cb6b5d69183ba279e97382ff68a071864c25b561d206dab73499d3ed26d1c",
|
||||
"sha256:d757b78fd7d3c6bb60e3ee811b68145583643747ed3ec253329f086aa3a72a5d"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.0.6"
|
||||
"version": "==5.0.7"
|
||||
},
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
|
@ -169,19 +169,19 @@
|
|||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:4cdaca9699a266936c04543700a5c6cdf52b563d33703812459cfe42c4c63ace",
|
||||
"sha256:c8fbacb7d4e90a17cfe2a68e728c574e5b4a97ab66de417a44c373f7236366a1"
|
||||
"sha256:76b3ee0d1dd860c9218bc864cd29f1ee986f6e1e75e8669725dd3c411039379e",
|
||||
"sha256:c39cb6ed376ba1d4689ac8f6759a2b2d8a0b0424dbec0cd3af1558079bcf06e8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.20.22"
|
||||
"version": "==1.20.23"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:6738e87baa48e4befc447dded787fcadb87a23efb3a5ee6bfe78177bc9630516",
|
||||
"sha256:e66e4905dc048d5109df2cc6db55772b9111bfab8907557fa610f9241dca8752"
|
||||
"sha256:640b62110aa6d1c25553eceafb5bcd89aedeb84b191598d1f6492ad24374d285",
|
||||
"sha256:7459766c4594f3b8877e8013f93f0dc6c6486acbeb7d9c9ae488396529cc2e84"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.23.22"
|
||||
"version": "==1.23.23"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
|
@ -191,6 +191,14 @@
|
|||
"markers": "python_version ~= '3.5'",
|
||||
"version": "==4.2.4"
|
||||
},
|
||||
"cattrs": {
|
||||
"hashes": [
|
||||
"sha256:1ef33f089e0a494e8d1b487508356f055c865b1955b125c00c991a4358543c80",
|
||||
"sha256:8eca49962b1bfc09c24d442aa55688be88efe5c24aeef89d3be135614b95c678"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||
"version": "==1.9.0"
|
||||
},
|
||||
"cbor2": {
|
||||
"hashes": [
|
||||
"sha256:0153635a78e62d70f26f5b3469cb8de822420eda69c996304fb3d0dc1a53d7f3",
|
||||
|
@ -317,7 +325,7 @@
|
|||
"sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
|
||||
"sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
|
||||
],
|
||||
"markers": "python_version < '4' and python_full_version >= '3.6.2'",
|
||||
"markers": "python_version < '4.0' and python_full_version >= '3.6.2'",
|
||||
"version": "==0.3.0"
|
||||
},
|
||||
"click-plugins": {
|
||||
|
@ -1150,34 +1158,6 @@
|
|||
"index": "pypi",
|
||||
"version": "==3.12.0"
|
||||
},
|
||||
"pydantic": {
|
||||
"hashes": [
|
||||
"sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd",
|
||||
"sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739",
|
||||
"sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f",
|
||||
"sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840",
|
||||
"sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23",
|
||||
"sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287",
|
||||
"sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62",
|
||||
"sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b",
|
||||
"sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb",
|
||||
"sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820",
|
||||
"sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3",
|
||||
"sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b",
|
||||
"sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e",
|
||||
"sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3",
|
||||
"sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316",
|
||||
"sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b",
|
||||
"sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4",
|
||||
"sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20",
|
||||
"sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e",
|
||||
"sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505",
|
||||
"sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1",
|
||||
"sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.1'",
|
||||
"version": "==1.8.2"
|
||||
},
|
||||
"pyjwt": {
|
||||
"hashes": [
|
||||
"sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41",
|
||||
|
@ -1191,6 +1171,7 @@
|
|||
"sha256:5e2d8c5e46d0d865ae933bef5230090bdaf5506281e9eec60fa250ee80600cb3",
|
||||
"sha256:8935bd4920ab9abfebb07c41a4f58296407ed77f04bd1a92914044b848ba1ed6"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==21.0.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
|
@ -1331,12 +1312,12 @@
|
|||
"version": "==0.5.0"
|
||||
},
|
||||
"sentry-sdk": {
|
||||
"git": "https://github.com/BeryJu/sentry-python.git",
|
||||
"hashes": [
|
||||
"sha256:0db297ab32e095705c20f742c3a5dac62fe15c4318681884053d0898e5abb2f6",
|
||||
"sha256:789a11a87ca02491896e121efdd64e8fd93327b69e8f2f7d42f03e2569648e88"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.0"
|
||||
"ref": "bba7d80c05bc6845aa333ebbd87e3b76747ed355"
|
||||
},
|
||||
"service-identity": {
|
||||
"hashes": [
|
||||
|
@ -1348,11 +1329,11 @@
|
|||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:6d10741ff20b89cd8c6a536ee9dc90d3002dec0226c78fb98605bfb9ef8a7adf",
|
||||
"sha256:d144f85102f999444d06f9c0e8c737fd0194f10f2f7e5fdb77573f6e2fa4fad0"
|
||||
"sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373",
|
||||
"sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==59.5.0"
|
||||
"version": "==59.6.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -1540,11 +1521,11 @@
|
|||
},
|
||||
"webauthn": {
|
||||
"hashes": [
|
||||
"sha256:1a24696c67f8f3eb09267b10b3bfa4aade86bf08e2c41333d8c00f199c528b97",
|
||||
"sha256:523e6e488a4d5e8c4fe183dde154a7cdfc9dad62e6e30e97c1d52e4a4ae6390e"
|
||||
"sha256:681b71d803c085d0911e29f2e2bbb0e49feda1ecdaa3982bae3d63b3377efdda",
|
||||
"sha256:ecae0f99a6dc1a6d53e72f4be323cf58b418c02fe66dffae752eedc1d2ee6a70"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.1.0"
|
||||
"version": "==1.2.0"
|
||||
},
|
||||
"websocket-client": {
|
||||
"hashes": [
|
||||
|
@ -1607,6 +1588,14 @@
|
|||
],
|
||||
"version": "==10.1"
|
||||
},
|
||||
"wsproto": {
|
||||
"hashes": [
|
||||
"sha256:868776f8456997ad0d9720f7322b746bbe9193751b5b290b7f924659377c8c38",
|
||||
"sha256:d8345d1808dd599b5ffb352c25a367adb6157e664e140dbecba3f9bc007edb9f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"xmlsec": {
|
||||
"hashes": [
|
||||
"sha256:135724cdce60e6bbd072fca6f09a21f72e2cecc59eebb4eed7740c316ecabc7b",
|
||||
|
@ -2029,7 +2018,7 @@
|
|||
"sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
|
||||
"sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
|
||||
],
|
||||
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
|
||||
"markers": "python_version < '4' and python_full_version >= '3.6.1'",
|
||||
"version": "==5.10.1"
|
||||
},
|
||||
"lazy-object-proxy": {
|
||||
|
@ -2165,6 +2154,7 @@
|
|||
"sha256:5e2d8c5e46d0d865ae933bef5230090bdaf5506281e9eec60fa250ee80600cb3",
|
||||
"sha256:8935bd4920ab9abfebb07c41a4f58296407ed77f04bd1a92914044b848ba1ed6"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==21.0.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
|
@ -2342,11 +2332,11 @@
|
|||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:6d10741ff20b89cd8c6a536ee9dc90d3002dec0226c78fb98605bfb9ef8a7adf",
|
||||
"sha256:d144f85102f999444d06f9c0e8c737fd0194f10f2f7e5fdb77573f6e2fa4fad0"
|
||||
"sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373",
|
||||
"sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==59.5.0"
|
||||
"version": "==59.6.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -2500,7 +2490,7 @@
|
|||
"sha256:868776f8456997ad0d9720f7322b746bbe9193751b5b290b7f924659377c8c38",
|
||||
"sha256:d8345d1808dd599b5ffb352c25a367adb6157e664e140dbecba3f9bc007edb9f"
|
||||
],
|
||||
"markers": "python_full_version >= '3.6.1'",
|
||||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"zipp": {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
"""Groups API Viewset"""
|
||||
from json import loads
|
||||
|
||||
from django.db.models.query import QuerySet
|
||||
from django_filters.filters import ModelMultipleChoiceFilter
|
||||
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from rest_framework.fields import CharField, JSONField
|
||||
from rest_framework.serializers import ListSerializer, ModelSerializer
|
||||
from rest_framework.serializers import ListSerializer, ModelSerializer, ValidationError
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework_guardian.filters import ObjectPermissionsFilter
|
||||
|
||||
|
@ -62,6 +64,13 @@ class GroupSerializer(ModelSerializer):
|
|||
class GroupFilter(FilterSet):
|
||||
"""Filter for groups"""
|
||||
|
||||
attributes = CharFilter(
|
||||
field_name="attributes",
|
||||
lookup_expr="",
|
||||
label="Attributes",
|
||||
method="filter_attributes",
|
||||
)
|
||||
|
||||
members_by_username = ModelMultipleChoiceFilter(
|
||||
field_name="users__username",
|
||||
to_field_name="username",
|
||||
|
@ -72,10 +81,28 @@ class GroupFilter(FilterSet):
|
|||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def filter_attributes(self, queryset, name, value):
|
||||
"""Filter attributes by query args"""
|
||||
try:
|
||||
value = loads(value)
|
||||
except ValueError:
|
||||
raise ValidationError(detail="filter: failed to parse JSON")
|
||||
if not isinstance(value, dict):
|
||||
raise ValidationError(detail="filter: value must be key:value mapping")
|
||||
qs = {}
|
||||
for key, _value in value.items():
|
||||
qs[f"attributes__{key}"] = _value
|
||||
try:
|
||||
_ = len(queryset.filter(**qs))
|
||||
return queryset.filter(**qs)
|
||||
except ValueError:
|
||||
return queryset
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Group
|
||||
fields = ["name", "is_superuser", "members_by_pk", "members_by_username"]
|
||||
fields = ["name", "is_superuser", "members_by_pk", "attributes", "members_by_username"]
|
||||
|
||||
|
||||
class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||
|
|
|
@ -233,7 +233,11 @@ class UsersFilter(FilterSet):
|
|||
qs = {}
|
||||
for key, _value in value.items():
|
||||
qs[f"attributes__{key}"] = _value
|
||||
return queryset.filter(**qs)
|
||||
try:
|
||||
_ = len(queryset.filter(**qs))
|
||||
return queryset.filter(**qs)
|
||||
except ValueError:
|
||||
return queryset
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
|
|
@ -285,7 +285,7 @@ class FlowToken(Token):
|
|||
return loads(b64decode(self._plan.encode())) # nosec
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Flow Token {super.__str__()}"
|
||||
return f"Flow Token {super().__str__()}"
|
||||
|
||||
class Meta:
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ class FlowPlanner:
|
|||
) -> FlowPlan:
|
||||
"""Check each of the flows' policies, check policies for each stage with PolicyBinding
|
||||
and return ordered list"""
|
||||
with Hub.current.start_span(op="flow.planner.plan") as span:
|
||||
with Hub.current.start_span(op="flow.planner.plan", description=self.flow.slug) as span:
|
||||
span: Span
|
||||
span.set_data("flow", self.flow)
|
||||
span.set_data("request", request)
|
||||
|
@ -181,7 +181,8 @@ class FlowPlanner:
|
|||
"""Build flow plan by checking each stage in their respective
|
||||
order and checking the applied policies"""
|
||||
with Hub.current.start_span(
|
||||
op="flow.planner.build_plan"
|
||||
op="flow.planner.build_plan",
|
||||
description=self.flow.slug,
|
||||
) as span, HIST_FLOWS_PLAN_TIME.labels(flow_slug=self.flow.slug).time():
|
||||
span: Span
|
||||
span.set_data("flow", self.flow)
|
||||
|
|
|
@ -19,6 +19,8 @@ from drf_spectacular.utils import OpenApiParameter, PolymorphicProxySerializer,
|
|||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.views import APIView
|
||||
from sentry_sdk import capture_exception
|
||||
from sentry_sdk.api import set_tag
|
||||
from sentry_sdk.hub import Hub
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.models import USER_ATTRIBUTE_DEBUG
|
||||
|
@ -126,6 +128,7 @@ class FlowExecutorView(APIView):
|
|||
super().setup(request, flow_slug=flow_slug)
|
||||
self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug)
|
||||
self._logger = get_logger().bind(flow_slug=flow_slug)
|
||||
set_tag("authentik.flow", self.flow.slug)
|
||||
|
||||
def handle_invalid_flow(self, exc: BaseException) -> HttpResponse:
|
||||
"""When a flow is non-applicable check if user is on the correct domain"""
|
||||
|
@ -156,74 +159,80 @@ class FlowExecutorView(APIView):
|
|||
|
||||
# pylint: disable=unused-argument, too-many-return-statements
|
||||
def dispatch(self, request: HttpRequest, flow_slug: str) -> HttpResponse:
|
||||
get_params = QueryDict(request.GET.get("query", ""))
|
||||
if QS_KEY_TOKEN in get_params:
|
||||
plan = self._check_flow_token(get_params)
|
||||
if plan:
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
# Early check if there's an active Plan for the current session
|
||||
if SESSION_KEY_PLAN in self.request.session:
|
||||
self.plan = self.request.session[SESSION_KEY_PLAN]
|
||||
if self.plan.flow_pk != self.flow.pk.hex:
|
||||
self._logger.warning(
|
||||
"f(exec): Found existing plan for other flow, deleting plan",
|
||||
)
|
||||
# Existing plan is deleted from session and instance
|
||||
self.plan = None
|
||||
self.cancel()
|
||||
self._logger.debug("f(exec): Continuing existing plan")
|
||||
with Hub.current.start_span(
|
||||
op="flow.executor.dispatch", description=self.flow.slug
|
||||
) as span:
|
||||
span.set_data("authentik Flow", self.flow.slug)
|
||||
get_params = QueryDict(request.GET.get("query", ""))
|
||||
if QS_KEY_TOKEN in get_params:
|
||||
plan = self._check_flow_token(get_params)
|
||||
if plan:
|
||||
self.request.session[SESSION_KEY_PLAN] = plan
|
||||
# Early check if there's an active Plan for the current session
|
||||
if SESSION_KEY_PLAN in self.request.session:
|
||||
self.plan = self.request.session[SESSION_KEY_PLAN]
|
||||
if self.plan.flow_pk != self.flow.pk.hex:
|
||||
self._logger.warning(
|
||||
"f(exec): Found existing plan for other flow, deleting plan",
|
||||
)
|
||||
# Existing plan is deleted from session and instance
|
||||
self.plan = None
|
||||
self.cancel()
|
||||
self._logger.debug("f(exec): Continuing existing plan")
|
||||
|
||||
# Don't check session again as we've either already loaded the plan or we need to plan
|
||||
if not self.plan:
|
||||
request.session[SESSION_KEY_HISTORY] = []
|
||||
self._logger.debug("f(exec): No active Plan found, initiating planner")
|
||||
# Don't check session again as we've either already loaded the plan or we need to plan
|
||||
if not self.plan:
|
||||
request.session[SESSION_KEY_HISTORY] = []
|
||||
self._logger.debug("f(exec): No active Plan found, initiating planner")
|
||||
try:
|
||||
self.plan = self._initiate_plan()
|
||||
except FlowNonApplicableException as exc:
|
||||
self._logger.warning("f(exec): Flow not applicable to current user", exc=exc)
|
||||
return to_stage_response(self.request, self.handle_invalid_flow(exc))
|
||||
except EmptyFlowException as exc:
|
||||
self._logger.warning("f(exec): Flow is empty", exc=exc)
|
||||
# To match behaviour with loading an empty flow plan from cache,
|
||||
# we don't show an error message here, but rather call _flow_done()
|
||||
return self._flow_done()
|
||||
# Initial flow request, check if we have an upstream query string passed in
|
||||
request.session[SESSION_KEY_GET] = get_params
|
||||
# We don't save the Plan after getting the next stage
|
||||
# as it hasn't been successfully passed yet
|
||||
try:
|
||||
self.plan = self._initiate_plan()
|
||||
except FlowNonApplicableException as exc:
|
||||
self._logger.warning("f(exec): Flow not applicable to current user", exc=exc)
|
||||
return to_stage_response(self.request, self.handle_invalid_flow(exc))
|
||||
except EmptyFlowException as exc:
|
||||
self._logger.warning("f(exec): Flow is empty", exc=exc)
|
||||
# To match behaviour with loading an empty flow plan from cache,
|
||||
# we don't show an error message here, but rather call _flow_done()
|
||||
# This is the first time we actually access any attribute on the selected plan
|
||||
# if the cached plan is from an older version, it might have different attributes
|
||||
# in which case we just delete the plan and invalidate everything
|
||||
next_binding = self.plan.next(self.request)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
self._logger.warning(
|
||||
"f(exec): found incompatible flow plan, invalidating run", exc=exc
|
||||
)
|
||||
keys = cache.keys("flow_*")
|
||||
cache.delete_many(keys)
|
||||
return self.stage_invalid()
|
||||
if not next_binding:
|
||||
self._logger.debug("f(exec): no more stages, flow is done.")
|
||||
return self._flow_done()
|
||||
# Initial flow request, check if we have an upstream query string passed in
|
||||
request.session[SESSION_KEY_GET] = get_params
|
||||
# We don't save the Plan after getting the next stage
|
||||
# as it hasn't been successfully passed yet
|
||||
try:
|
||||
# This is the first time we actually access any attribute on the selected plan
|
||||
# if the cached plan is from an older version, it might have different attributes
|
||||
# in which case we just delete the plan and invalidate everything
|
||||
next_binding = self.plan.next(self.request)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
self._logger.warning("f(exec): found incompatible flow plan, invalidating run", exc=exc)
|
||||
keys = cache.keys("flow_*")
|
||||
cache.delete_many(keys)
|
||||
return self.stage_invalid()
|
||||
if not next_binding:
|
||||
self._logger.debug("f(exec): no more stages, flow is done.")
|
||||
return self._flow_done()
|
||||
self.current_binding = next_binding
|
||||
self.current_stage = next_binding.stage
|
||||
self._logger.debug(
|
||||
"f(exec): Current stage",
|
||||
current_stage=self.current_stage,
|
||||
flow_slug=self.flow.slug,
|
||||
)
|
||||
try:
|
||||
stage_cls = self.current_stage.type
|
||||
except NotImplementedError as exc:
|
||||
self._logger.debug("Error getting stage type", exc=exc)
|
||||
return self.stage_invalid()
|
||||
self.current_stage_view = stage_cls(self)
|
||||
self.current_stage_view.args = self.args
|
||||
self.current_stage_view.kwargs = self.kwargs
|
||||
self.current_stage_view.request = request
|
||||
try:
|
||||
return super().dispatch(request)
|
||||
except InvalidStageError as exc:
|
||||
return self.stage_invalid(str(exc))
|
||||
self.current_binding = next_binding
|
||||
self.current_stage = next_binding.stage
|
||||
self._logger.debug(
|
||||
"f(exec): Current stage",
|
||||
current_stage=self.current_stage,
|
||||
flow_slug=self.flow.slug,
|
||||
)
|
||||
try:
|
||||
stage_cls = self.current_stage.type
|
||||
except NotImplementedError as exc:
|
||||
self._logger.debug("Error getting stage type", exc=exc)
|
||||
return self.stage_invalid()
|
||||
self.current_stage_view = stage_cls(self)
|
||||
self.current_stage_view.args = self.args
|
||||
self.current_stage_view.kwargs = self.kwargs
|
||||
self.current_stage_view.request = request
|
||||
try:
|
||||
return super().dispatch(request)
|
||||
except InvalidStageError as exc:
|
||||
return self.stage_invalid(str(exc))
|
||||
|
||||
def handle_exception(self, exc: Exception) -> HttpResponse:
|
||||
"""Handle exception in stage execution"""
|
||||
|
@ -265,8 +274,15 @@ class FlowExecutorView(APIView):
|
|||
stage=self.current_stage,
|
||||
)
|
||||
try:
|
||||
stage_response = self.current_stage_view.get(request, *args, **kwargs)
|
||||
return to_stage_response(request, stage_response)
|
||||
with Hub.current.start_span(
|
||||
op="flow.executor.stage",
|
||||
description=class_to_path(self.current_stage_view.__class__),
|
||||
) as span:
|
||||
span.set_data("Method", "GET")
|
||||
span.set_data("authentik Stage", self.current_stage_view)
|
||||
span.set_data("authentik Flow", self.flow.slug)
|
||||
stage_response = self.current_stage_view.get(request, *args, **kwargs)
|
||||
return to_stage_response(request, stage_response)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
return self.handle_exception(exc)
|
||||
|
||||
|
@ -302,8 +318,15 @@ class FlowExecutorView(APIView):
|
|||
stage=self.current_stage,
|
||||
)
|
||||
try:
|
||||
stage_response = self.current_stage_view.post(request, *args, **kwargs)
|
||||
return to_stage_response(request, stage_response)
|
||||
with Hub.current.start_span(
|
||||
op="flow.executor.stage",
|
||||
description=class_to_path(self.current_stage_view.__class__),
|
||||
) as span:
|
||||
span.set_data("Method", "POST")
|
||||
span.set_data("authentik Stage", self.current_stage_view)
|
||||
span.set_data("authentik Flow", self.flow.slug)
|
||||
stage_response = self.current_stage_view.post(request, *args, **kwargs)
|
||||
return to_stage_response(request, stage_response)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
return self.handle_exception(exc)
|
||||
|
||||
|
|
|
@ -87,9 +87,7 @@ class FlowInspectorView(APIView):
|
|||
@extend_schema(
|
||||
responses={
|
||||
200: FlowInspectionSerializer(),
|
||||
400: OpenApiResponse(
|
||||
description="No flow plan in session."
|
||||
), # This error can be raised by the email stage
|
||||
400: OpenApiResponse(description="No flow plan in session."),
|
||||
},
|
||||
request=OpenApiTypes.NONE,
|
||||
operation_id="flows_inspector_get",
|
||||
|
@ -106,7 +104,10 @@ class FlowInspectorView(APIView):
|
|||
if SESSION_KEY_PLAN in request.session:
|
||||
current_plan: FlowPlan = request.session[SESSION_KEY_PLAN]
|
||||
else:
|
||||
current_plan = request.session[SESSION_KEY_HISTORY][-1]
|
||||
try:
|
||||
current_plan = request.session[SESSION_KEY_HISTORY][-1]
|
||||
except KeyError:
|
||||
return Response(status=400)
|
||||
is_completed = True
|
||||
current_serializer = FlowInspectorPlanSerializer(
|
||||
instance=current_plan, context={"request": request}
|
||||
|
|
|
@ -20,7 +20,6 @@ web:
|
|||
listen: 0.0.0.0:9000
|
||||
listen_tls: 0.0.0.0:9443
|
||||
listen_metrics: 0.0.0.0:9300
|
||||
load_local_files: false
|
||||
outpost_port_offset: 0
|
||||
|
||||
redis:
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Any, Optional
|
|||
|
||||
from django.http import HttpRequest
|
||||
from requests.sessions import Session
|
||||
from sentry_sdk.hub import Hub
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik import ENV_GIT_HASH_KEY, __version__
|
||||
|
@ -52,6 +53,12 @@ def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
|
|||
fake_ip=fake_ip,
|
||||
)
|
||||
return None
|
||||
# Update sentry scope to include correct IP
|
||||
user = Hub.current.scope._user
|
||||
if not user:
|
||||
user = {}
|
||||
user["ip_address"] = fake_ip
|
||||
Hub.current.scope.set_user(user)
|
||||
return fake_ip
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from dacite import from_dict
|
|||
from dacite.data import Data
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from prometheus_client import Gauge
|
||||
from structlog.stdlib import get_logger
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.channels import AuthJsonConsumer
|
||||
from authentik.outposts.models import OUTPOST_HELLO_INTERVAL, Outpost, OutpostState
|
||||
|
@ -23,8 +23,6 @@ GAUGE_OUTPOSTS_LAST_UPDATE = Gauge(
|
|||
["outpost", "uid", "version"],
|
||||
)
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class WebsocketMessageInstruction(IntEnum):
|
||||
"""Commands which can be triggered over Websocket"""
|
||||
|
@ -51,6 +49,7 @@ class OutpostConsumer(AuthJsonConsumer):
|
|||
"""Handler for Outposts that connect over websockets for health checks and live updates"""
|
||||
|
||||
outpost: Optional[Outpost] = None
|
||||
logger: BoundLogger
|
||||
|
||||
last_uid: Optional[str] = None
|
||||
|
||||
|
@ -59,11 +58,20 @@ class OutpostConsumer(AuthJsonConsumer):
|
|||
def connect(self):
|
||||
super().connect()
|
||||
uuid = self.scope["url_route"]["kwargs"]["pk"]
|
||||
outpost = get_objects_for_user(self.user, "authentik_outposts.view_outpost").filter(pk=uuid)
|
||||
if not outpost.exists():
|
||||
outpost = (
|
||||
get_objects_for_user(self.user, "authentik_outposts.view_outpost")
|
||||
.filter(pk=uuid)
|
||||
.first()
|
||||
)
|
||||
if not outpost:
|
||||
raise DenyConnection()
|
||||
self.accept()
|
||||
self.outpost = outpost.first()
|
||||
self.logger = get_logger().bind(outpost=outpost)
|
||||
try:
|
||||
self.accept()
|
||||
except RuntimeError as exc:
|
||||
self.logger.warning("runtime error during accept", exc=exc)
|
||||
raise DenyConnection()
|
||||
self.outpost = outpost
|
||||
self.last_uid = self.channel_name
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -78,9 +86,8 @@ class OutpostConsumer(AuthJsonConsumer):
|
|||
uid=self.last_uid,
|
||||
expected=self.outpost.config.kubernetes_replicas,
|
||||
).dec()
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"removed outpost instance from cache",
|
||||
outpost=self.outpost,
|
||||
instance_uuid=self.last_uid,
|
||||
)
|
||||
|
||||
|
@ -103,9 +110,8 @@ class OutpostConsumer(AuthJsonConsumer):
|
|||
uid=self.last_uid,
|
||||
expected=self.outpost.config.kubernetes_replicas,
|
||||
).inc()
|
||||
LOGGER.debug(
|
||||
self.logger.debug(
|
||||
"added outpost instance to cache",
|
||||
outpost=self.outpost,
|
||||
instance_uuid=self.last_uid,
|
||||
)
|
||||
self.first_msg = True
|
||||
|
|
|
@ -24,6 +24,8 @@ class DockerController(BaseController):
|
|||
|
||||
def __init__(self, outpost: Outpost, connection: DockerServiceConnection) -> None:
|
||||
super().__init__(outpost, connection)
|
||||
if outpost.managed == MANAGED_OUTPOST:
|
||||
return
|
||||
try:
|
||||
self.client = connection.client()
|
||||
except ServiceConnectionInvalid as exc:
|
||||
|
@ -225,12 +227,14 @@ class DockerController(BaseController):
|
|||
raise ControllerException(str(exc)) from exc
|
||||
|
||||
def down(self):
|
||||
if self.outpost.managed != MANAGED_OUTPOST:
|
||||
if self.outpost.managed == MANAGED_OUTPOST:
|
||||
return
|
||||
try:
|
||||
container, _ = self._get_container()
|
||||
if container.status == "running":
|
||||
self.logger.info("Stopping container.")
|
||||
container.kill()
|
||||
self.logger.info("Removing container.")
|
||||
container.remove(force=True)
|
||||
except DockerException as exc:
|
||||
raise ControllerException(str(exc)) from exc
|
||||
|
|
|
@ -401,6 +401,7 @@ class Outpost(ManagedModel):
|
|||
user = users.first()
|
||||
user.attributes[USER_ATTRIBUTE_SA] = True
|
||||
user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
|
||||
user.name = f"Outpost {self.name} Service-Account"
|
||||
user.save()
|
||||
if should_create_user:
|
||||
self.build_user_permissions(user)
|
||||
|
|
|
@ -105,9 +105,12 @@ def outpost_controller(
|
|||
logs = []
|
||||
if from_cache:
|
||||
outpost: Outpost = cache.get(CACHE_KEY_OUTPOST_DOWN % outpost_pk)
|
||||
LOGGER.debug("Getting outpost from cache to delete")
|
||||
else:
|
||||
outpost: Outpost = Outpost.objects.filter(pk=outpost_pk).first()
|
||||
LOGGER.debug("Getting outpost from DB")
|
||||
if not outpost:
|
||||
LOGGER.warning("No outpost")
|
||||
return
|
||||
self.set_uid(slugify(outpost.name))
|
||||
try:
|
||||
|
|
|
@ -90,7 +90,8 @@ class PolicyEngine:
|
|||
def build(self) -> "PolicyEngine":
|
||||
"""Build wrapper which monitors performance"""
|
||||
with Hub.current.start_span(
|
||||
op="policy.engine.build"
|
||||
op="policy.engine.build",
|
||||
description=self.__pbm,
|
||||
) as span, HIST_POLICIES_BUILD_TIME.labels(
|
||||
object_name=self.__pbm,
|
||||
object_type=f"{self.__pbm._meta.app_label}.{self.__pbm._meta.model_name}",
|
||||
|
|
|
@ -23,6 +23,6 @@ def invalidate_policy_cache(sender, instance, **_):
|
|||
total += len(keys)
|
||||
cache.delete_many(keys)
|
||||
LOGGER.debug("Invalidating policy cache", policy=instance, keys=total)
|
||||
# Also delete user application cache
|
||||
keys = cache.keys(user_app_cache_key("*")) or []
|
||||
cache.delete_many(keys)
|
||||
# Also delete user application cache
|
||||
keys = cache.keys(user_app_cache_key("*")) or []
|
||||
cache.delete_many(keys)
|
||||
|
|
|
@ -37,7 +37,7 @@ def config_loggers(*args, **kwargs):
|
|||
def after_task_publish_hook(sender=None, headers=None, body=None, **kwargs):
|
||||
"""Log task_id after it was published"""
|
||||
info = headers if "task" in headers else body
|
||||
LOGGER.debug("Task published", task_id=info.get("id", ""), task_name=info.get("task", ""))
|
||||
LOGGER.info("Task published", task_id=info.get("id", ""), task_name=info.get("task", ""))
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
@ -48,14 +48,14 @@ def task_prerun_hook(task_id: str, task, *args, **kwargs):
|
|||
LOCAL.authentik_task = {
|
||||
"request_id": request_id,
|
||||
}
|
||||
LOGGER.debug("Task started", task_id=task_id, task_name=task.__name__)
|
||||
LOGGER.info("Task started", task_id=task_id, task_name=task.__name__)
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
@task_postrun.connect
|
||||
def task_postrun_hook(task_id, task, *args, retval=None, state=None, **kwargs):
|
||||
"""Log task_id on worker"""
|
||||
LOGGER.debug("Task finished", task_id=task_id, task_name=task.__name__, state=state)
|
||||
LOGGER.info("Task finished", task_id=task_id, task_name=task.__name__, state=state)
|
||||
if not hasattr(LOCAL, "authentik_task"):
|
||||
return
|
||||
for key in list(LOCAL.authentik_task.keys()):
|
||||
|
|
|
@ -24,6 +24,7 @@ import structlog
|
|||
from celery.schedules import crontab
|
||||
from sentry_sdk import init as sentry_init
|
||||
from sentry_sdk.api import set_tag
|
||||
from sentry_sdk.integrations.boto3 import Boto3Integration
|
||||
from sentry_sdk.integrations.celery import CeleryIntegration
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
from sentry_sdk.integrations.redis import RedisIntegration
|
||||
|
@ -231,6 +232,7 @@ CACHES = {
|
|||
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
||||
}
|
||||
}
|
||||
DJANGO_REDIS_SCAN_ITERSIZE = 1000
|
||||
DJANGO_REDIS_IGNORE_EXCEPTIONS = True
|
||||
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
|
@ -421,6 +423,7 @@ if _ERROR_REPORTING:
|
|||
DjangoIntegration(transaction_style="function_name"),
|
||||
CeleryIntegration(),
|
||||
RedisIntegration(),
|
||||
Boto3Integration(),
|
||||
],
|
||||
before_send=before_send,
|
||||
release=f"authentik@{__version__}",
|
||||
|
|
|
@ -30,7 +30,7 @@ class PytestTestRunner: # pragma: no cover
|
|||
CONFIG.y_set("authentik.geoip", "tests/GeoLite2-City-Test.mmdb")
|
||||
CONFIG.y_set(
|
||||
"outposts.container_image_base",
|
||||
f"goauthentik.io/dev-%(type)s:{get_docker_tag()}",
|
||||
f"ghcr.io/goauthentik/dev-%(type)s:{get_docker_tag()}",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -53,9 +53,6 @@ class AuthenticatorWebAuthnChallengeResponse(ChallengeResponse):
|
|||
|
||||
def validate_response(self, response: dict) -> dict:
|
||||
"""Validate webauthn challenge response"""
|
||||
# pylint: disable=no-name-in-module
|
||||
from pydantic.error_wrappers import ValidationError as PydanticValidationError
|
||||
|
||||
challenge = self.request.session["challenge"]
|
||||
|
||||
try:
|
||||
|
@ -65,7 +62,7 @@ class AuthenticatorWebAuthnChallengeResponse(ChallengeResponse):
|
|||
expected_rp_id=get_rp_id(self.request),
|
||||
expected_origin=get_origin(self.request),
|
||||
)
|
||||
except (InvalidRegistrationResponse, PydanticValidationError) as exc:
|
||||
except InvalidRegistrationResponse as exc:
|
||||
LOGGER.warning("registration failed", exc=exc)
|
||||
raise ValidationError(f"Registration failed. Error: {exc}")
|
||||
|
||||
|
|
3
go.mod
3
go.mod
|
@ -27,9 +27,8 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/cachecontrol v0.0.0-20201205024021-ac21108117ac // indirect
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/recws-org/recws v1.3.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
goauthentik.io/api v0.2021104.10
|
||||
goauthentik.io/api v0.2021104.11
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558
|
||||
|
|
7
go.sum
7
go.sum
|
@ -356,7 +356,6 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
|
|||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -481,8 +480,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
|||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/recws-org/recws v1.3.1 h1:vtRhYpgNPBs3iFyu/+zxBqNzLYgID7UPC5siThkvbs0=
|
||||
github.com/recws-org/recws v1.3.1/go.mod h1:gRH/uJLMsO7lbcecAB1Im1Zc6eKxs93ftGR0R39QeYA=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
@ -561,8 +558,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
goauthentik.io/api v0.2021104.10 h1:5A2KLhwe5uSkPPiZDg8td3OLFKxcODSMqkyvRSavcUM=
|
||||
goauthentik.io/api v0.2021104.10/go.mod h1:02nnD4FRd8lu8A1+ZuzqownBgvAhdCKzqkKX8v7JMTE=
|
||||
goauthentik.io/api v0.2021104.11 h1:LqT0LM0e/RRrxPuo6Xl5uz3PCR5ytuE+YlNlfW9w0yU=
|
||||
goauthentik.io/api v0.2021104.11/go.mod h1:02nnD4FRd8lu8A1+ZuzqownBgvAhdCKzqkKX8v7JMTE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
|
|
@ -16,9 +16,8 @@ func DefaultConfig() {
|
|||
G = Config{
|
||||
Debug: false,
|
||||
Web: WebConfig{
|
||||
Listen: "localhost:9000",
|
||||
ListenTLS: "localhost:9443",
|
||||
LoadLocalFiles: false,
|
||||
Listen: "localhost:9000",
|
||||
ListenTLS: "localhost:9443",
|
||||
},
|
||||
Paths: PathsConfig{
|
||||
Media: "./media",
|
||||
|
|
|
@ -30,7 +30,6 @@ type WebConfig struct {
|
|||
Listen string `yaml:"listen"`
|
||||
ListenTLS string `yaml:"listen_tls"`
|
||||
ListenMetrics string `yaml:"listen_metrics"`
|
||||
LoadLocalFiles bool `yaml:"load_local_files" env:"AUTHENTIK_WEB_LOAD_LOCAL_FILES"`
|
||||
DisableEmbeddedOutpost bool `yaml:"disable_embedded_outpost" env:"AUTHENTIK_WEB__DISABLE_EMBEDDED_OUTPOST"`
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/recws-org/recws"
|
||||
"goauthentik.io/api"
|
||||
"goauthentik.io/internal/constants"
|
||||
|
||||
|
@ -35,20 +35,25 @@ type APIController struct {
|
|||
|
||||
logger *log.Entry
|
||||
|
||||
reloadOffset time.Duration
|
||||
lastWsReconnect time.Time
|
||||
reloadOffset time.Duration
|
||||
|
||||
wsConn *websocket.Conn
|
||||
lastWsReconnect time.Time
|
||||
wsIsReconnecting bool
|
||||
wsBackoffMultiplier int
|
||||
|
||||
wsConn *recws.RecConn
|
||||
instanceUUID uuid.UUID
|
||||
}
|
||||
|
||||
// NewAPIController initialise new API Controller instance from URL and API token
|
||||
func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
rsp := sentry.StartSpan(context.TODO(), "authentik.outposts.init")
|
||||
|
||||
config := api.NewConfiguration()
|
||||
config.Host = akURL.Host
|
||||
config.Scheme = akURL.Scheme
|
||||
config.HTTPClient = &http.Client{
|
||||
Transport: NewUserAgentTransport(constants.OutpostUserAgent(), NewTracingTransport(context.TODO(), GetTLSTransport())),
|
||||
Transport: NewUserAgentTransport(constants.OutpostUserAgent(), NewTracingTransport(rsp.Context(), GetTLSTransport())),
|
||||
}
|
||||
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
|
@ -85,12 +90,16 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
|||
token: token,
|
||||
logger: log,
|
||||
|
||||
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
|
||||
instanceUUID: uuid.New(),
|
||||
Outpost: outpost,
|
||||
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
|
||||
instanceUUID: uuid.New(),
|
||||
Outpost: outpost,
|
||||
wsBackoffMultiplier: 1,
|
||||
}
|
||||
ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset")
|
||||
ac.initWS(akURL, strfmt.UUID(outpost.Pk))
|
||||
err = ac.initWS(akURL, outpost.Pk)
|
||||
if err != nil {
|
||||
go ac.reconnectWS()
|
||||
}
|
||||
ac.configureRefreshSignal()
|
||||
return ac
|
||||
}
|
||||
|
@ -148,10 +157,6 @@ func (a *APIController) StartBackgorundTasks() error {
|
|||
"version": constants.VERSION,
|
||||
"build": constants.BUILD(),
|
||||
}).Set(1)
|
||||
go func() {
|
||||
a.logger.Debug("Starting WS re-connector...")
|
||||
a.startWSReConnector()
|
||||
}()
|
||||
go func() {
|
||||
a.logger.Debug("Starting WS Handler...")
|
||||
a.startWSHandler()
|
||||
|
|
|
@ -6,18 +6,17 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/recws-org/recws"
|
||||
"goauthentik.io/internal/constants"
|
||||
)
|
||||
|
||||
func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
||||
pathTemplate := "%s://%s/ws/outpost/%s/"
|
||||
func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
|
||||
pathTemplate := "%s://%s/ws/outpost/%s/?%s"
|
||||
scheme := strings.ReplaceAll(akURL.Scheme, "http", "ws")
|
||||
|
||||
authHeader := fmt.Sprintf("Bearer %s", ac.token)
|
||||
|
@ -32,15 +31,19 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
|||
value = "false"
|
||||
}
|
||||
|
||||
ws := &recws.RecConn{
|
||||
NonVerbose: true,
|
||||
dialer := websocket.Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: strings.ToLower(value) == "true",
|
||||
},
|
||||
}
|
||||
ws.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID.String()), header)
|
||||
|
||||
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("Connecting to authentik")
|
||||
ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID, akURL.Query().Encode()), header)
|
||||
if err != nil {
|
||||
ac.logger.WithError(err).Warning("failed to connect websocket")
|
||||
return err
|
||||
}
|
||||
|
||||
ac.wsConn = ws
|
||||
// Send hello message with our version
|
||||
|
@ -52,11 +55,14 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
|||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
err := ws.WriteJSON(msg)
|
||||
err = ws.WriteJSON(msg)
|
||||
if err != nil {
|
||||
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik")
|
||||
return err
|
||||
}
|
||||
ac.lastWsReconnect = time.Now()
|
||||
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID).Debug("Successfully connected websocket")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown Gracefully stops all workers, disconnects from websocket
|
||||
|
@ -65,21 +71,43 @@ func (ac *APIController) Shutdown() {
|
|||
// waiting (with timeout) for the server to close the connection.
|
||||
err := ac.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
if err != nil {
|
||||
ac.logger.Println("write close:", err)
|
||||
ac.logger.WithError(err).Warning("failed to write close message")
|
||||
return
|
||||
}
|
||||
err = ac.wsConn.Close()
|
||||
if err != nil {
|
||||
ac.logger.WithError(err).Warning("failed to close websocket")
|
||||
}
|
||||
ac.logger.Info("finished shutdown")
|
||||
}
|
||||
|
||||
func (ac *APIController) startWSReConnector() {
|
||||
func (ac *APIController) reconnectWS() {
|
||||
if ac.wsIsReconnecting {
|
||||
return
|
||||
}
|
||||
ac.wsIsReconnecting = true
|
||||
u := url.URL{
|
||||
Host: ac.Client.GetConfig().Host,
|
||||
Scheme: ac.Client.GetConfig().Scheme,
|
||||
}
|
||||
attempt := 1
|
||||
for {
|
||||
time.Sleep(time.Second * 5)
|
||||
if ac.wsConn.IsConnected() {
|
||||
continue
|
||||
}
|
||||
if time.Since(ac.lastWsReconnect).Seconds() > 30 {
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
ac.logger.Info("Reconnecting websocket")
|
||||
ac.lastWsReconnect = time.Now()
|
||||
q := u.Query()
|
||||
q.Set("attempt", strconv.Itoa(attempt))
|
||||
u.RawQuery = q.Encode()
|
||||
err := ac.initWS(u, ac.Outpost.Pk)
|
||||
attempt += 1
|
||||
if err != nil {
|
||||
ac.logger.Infof("waiting %d seconds to reconnect", ac.wsBackoffMultiplier)
|
||||
time.Sleep(time.Duration(ac.wsBackoffMultiplier) * time.Second)
|
||||
ac.wsBackoffMultiplier = ac.wsBackoffMultiplier * 2
|
||||
// Limit to 300 seconds (5m)
|
||||
if ac.wsBackoffMultiplier >= 300 {
|
||||
ac.wsBackoffMultiplier = 300
|
||||
}
|
||||
} else {
|
||||
ac.wsIsReconnecting = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +116,11 @@ func (ac *APIController) startWSHandler() {
|
|||
logger := ac.logger.WithField("loop", "ws-handler")
|
||||
for {
|
||||
var wsMsg websocketMessage
|
||||
if ac.wsConn == nil {
|
||||
go ac.reconnectWS()
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
err := ac.wsConn.ReadJSON(&wsMsg)
|
||||
if err != nil {
|
||||
ConnectionStatus.With(prometheus.Labels{
|
||||
|
@ -96,6 +129,7 @@ func (ac *APIController) startWSHandler() {
|
|||
"uuid": ac.instanceUUID.String(),
|
||||
}).Set(0)
|
||||
logger.WithError(err).Warning("ws read error")
|
||||
go ac.reconnectWS()
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
|
@ -126,9 +160,6 @@ func (ac *APIController) startWSHandler() {
|
|||
func (ac *APIController) startWSHealth() {
|
||||
ticker := time.NewTicker(time.Second * 10)
|
||||
for ; true; <-ticker.C {
|
||||
if !ac.wsConn.IsConnected() {
|
||||
continue
|
||||
}
|
||||
aliveMsg := websocketMessage{
|
||||
Instruction: WebsocketInstructionHello,
|
||||
Args: map[string]interface{}{
|
||||
|
@ -137,10 +168,16 @@ func (ac *APIController) startWSHealth() {
|
|||
"uuid": ac.instanceUUID.String(),
|
||||
},
|
||||
}
|
||||
if ac.wsConn == nil {
|
||||
go ac.reconnectWS()
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
err := ac.wsConn.WriteJSON(aliveMsg)
|
||||
ac.logger.WithField("loop", "ws-health").Trace("hello'd")
|
||||
if err != nil {
|
||||
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error")
|
||||
go ac.reconnectWS()
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
} else {
|
||||
|
|
|
@ -73,7 +73,7 @@ func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Config
|
|||
config.Scheme = refConfig.Scheme
|
||||
config.HTTPClient = &http.Client{
|
||||
Jar: jar,
|
||||
Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent(), ak.NewTracingTransport(ctx, ak.GetTLSTransport())),
|
||||
Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent(), ak.NewTracingTransport(rsp.Context(), ak.GetTLSTransport())),
|
||||
}
|
||||
token := strings.Split(refConfig.DefaultHeader["Authorization"], " ")[1]
|
||||
config.AddDefaultHeader(HeaderAuthentikOutpostToken, token)
|
||||
|
|
|
@ -52,7 +52,7 @@ func (ls *LDAPServer) StartLDAPServer() error {
|
|||
|
||||
ln, err := net.Listen("tcp", listen)
|
||||
if err != nil {
|
||||
ls.log.WithField("listen", listen).WithError(err).Fatalf("FATAL: listen failed")
|
||||
ls.log.WithField("listen", listen).WithError(err).Fatalf("listen failed")
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: ln}
|
||||
defer proxyListener.Close()
|
||||
|
|
|
@ -37,7 +37,7 @@ func (ls *LDAPServer) StartLDAPTLSServer() error {
|
|||
|
||||
ln, err := net.Listen("tcp", listen)
|
||||
if err != nil {
|
||||
ls.log.WithField("listen", listen).WithError(err).Fatalf("FATAL: listen failed")
|
||||
ls.log.WithField("listen", listen).WithError(err).Fatalf("listen failed")
|
||||
}
|
||||
|
||||
proxyListener := &proxyproto.Listener{Listener: ln}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ldap
|
|||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
|
@ -41,13 +42,13 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
|
|||
if searchReq.BaseDN == "" {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
bd, err := goldap.ParseDN(searchReq.BaseDN)
|
||||
bd, err := goldap.ParseDN(strings.ToLower(searchReq.BaseDN))
|
||||
if err != nil {
|
||||
req.Log().WithError(err).Info("failed to parse basedn")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, errors.New("invalid DN")
|
||||
}
|
||||
for _, provider := range ls.providers {
|
||||
providerBase, _ := goldap.ParseDN(provider.BaseDN)
|
||||
providerBase, _ := goldap.ParseDN(strings.ToLower(provider.BaseDN))
|
||||
if providerBase.AncestorOf(bd) || providerBase.Equal(bd) {
|
||||
return provider.searcher.Search(req)
|
||||
}
|
||||
|
|
|
@ -13,4 +13,6 @@ type Claims struct {
|
|||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Groups []string `json:"groups"`
|
||||
|
||||
RawToken string
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ func (a *Application) addHeaders(headers http.Header, c *Claims) {
|
|||
headers.Set("X-authentik-email", c.Email)
|
||||
headers.Set("X-authentik-name", c.Name)
|
||||
headers.Set("X-authentik-uid", c.Sub)
|
||||
headers.Set("X-authentik-jwt", c.RawToken)
|
||||
|
||||
// System headers
|
||||
headers.Set("X-authentik-meta-jwks", a.proxyConfig.OidcConfiguration.JwksUri)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/proxyv2/metrics"
|
||||
|
@ -28,7 +29,8 @@ func (a *Application) configureProxy() error {
|
|||
return err
|
||||
}
|
||||
rp := &httputil.ReverseProxy{Director: a.proxyModifyRequest(u)}
|
||||
rp.Transport = ak.NewTracingTransport(context.TODO(), a.getUpstreamTransport())
|
||||
rsp := sentry.StartSpan(context.TODO(), "authentik.outposts.proxy.application_transport")
|
||||
rp.Transport = ak.NewTracingTransport(rsp.Context(), a.getUpstreamTransport())
|
||||
rp.ErrorHandler = a.newProxyErrorHandler(templates.GetTemplates())
|
||||
rp.ModifyResponse = a.proxyModifyResponse
|
||||
a.mux.PathPrefix("/").HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -45,5 +45,6 @@ func (a *Application) redeemCallback(r *http.Request, shouldState string) (*Clai
|
|||
if err := idToken.Claims(&claims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claims.RawToken = rawIDToken
|
||||
return claims, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package application
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
|
@ -26,14 +27,14 @@ func (a *Application) getStore(p api.ProxyOutpostConfig) sessions.Store {
|
|||
a.log.Info("using redis session backend")
|
||||
store = rs
|
||||
} else {
|
||||
cs := sessions.NewCookieStore([]byte(*p.CookieSecret))
|
||||
cs := sessions.NewFilesystemStore(os.TempDir(), []byte(*p.CookieSecret))
|
||||
cs.Options.Domain = *p.CookieDomain
|
||||
if p.TokenValidity.IsSet() {
|
||||
t := p.TokenValidity.Get()
|
||||
// Add one to the validity to ensure we don't have a session with indefinite length
|
||||
cs.Options.MaxAge = int(*t) + 1
|
||||
}
|
||||
a.log.Info("using cookie session backend")
|
||||
a.log.Info("using filesystem session backend")
|
||||
store = cs
|
||||
}
|
||||
return store
|
||||
|
|
|
@ -103,7 +103,7 @@ func (ps *ProxyServer) ServeHTTP() {
|
|||
listenAddress := fmt.Sprintf(ps.Listen, 9000+ps.PortOffset)
|
||||
listener, err := net.Listen("tcp", listenAddress)
|
||||
if err != nil {
|
||||
ps.log.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err)
|
||||
ps.log.WithField("listen", listenAddress).WithError(err).Fatalf("listen failed")
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: listener}
|
||||
defer proxyListener.Close()
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"goauthentik.io/internal/constants"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
"goauthentik.io/internal/outpost/proxyv2/application"
|
||||
|
@ -20,9 +21,10 @@ func (ps *ProxyServer) Refresh() error {
|
|||
}
|
||||
apps := make(map[string]*application.Application)
|
||||
for _, provider := range providers.Results {
|
||||
rsp := sentry.StartSpan(context.Background(), "authentik.outposts.proxy.application_ss")
|
||||
ua := fmt.Sprintf(" (provider=%s)", provider.Name)
|
||||
hc := &http.Client{
|
||||
Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent()+ua, ak.NewTracingTransport(context.TODO(), ak.GetTLSTransport())),
|
||||
Transport: ak.NewUserAgentTransport(constants.OutpostUserAgent()+ua, ak.NewTracingTransport(rsp.Context(), ak.GetTLSTransport())),
|
||||
}
|
||||
a, err := application.NewApplication(provider, hc, ps.cryptoStore, ps.akAPI)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Stage 1: Build
|
||||
FROM docker.io/golang:1.17.4-bullseye AS builder
|
||||
FROM docker.io/golang:1.17.5-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ try:
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
worker_class = "uvicorn.workers.UvicornWorker"
|
||||
worker_class = "lifecycle.worker.DjangoUvicornWorker"
|
||||
# Docker containers don't have /tmp as tmpfs
|
||||
if os.path.exists("/dev/shm"): # nosec
|
||||
worker_tmp_dir = "/dev/shm" # nosec
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
"""Uvicorn worker"""
|
||||
from uvicorn.workers import UvicornWorker
|
||||
|
||||
|
||||
class DjangoUvicornWorker(UvicornWorker):
|
||||
"""Custom configured Uvicorn Worker without lifespan"""
|
||||
|
||||
CONFIG_KWARGS = {
|
||||
"loop": "uvloop",
|
||||
"http": "httptools",
|
||||
"lifespan": "off",
|
||||
"ws": "wsproto",
|
||||
}
|
|
@ -7,7 +7,7 @@ ENV NODE_ENV=production
|
|||
RUN cd /static && npm i && npm run build-proxy
|
||||
|
||||
# Stage 2: Build
|
||||
FROM docker.io/golang:1.17.4-bullseye AS builder
|
||||
FROM docker.io/golang:1.17.5-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
|
|
|
@ -2058,6 +2058,11 @@ paths:
|
|||
operationId: core_groups_list
|
||||
description: Group Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: attributes
|
||||
schema:
|
||||
type: string
|
||||
description: Attributes
|
||||
- in: query
|
||||
name: is_superuser
|
||||
schema:
|
||||
|
@ -11846,6 +11851,7 @@ paths:
|
|||
- ml
|
||||
- mn
|
||||
- mr
|
||||
- ms
|
||||
- my
|
||||
- nb
|
||||
- ne
|
||||
|
|
|
@ -34,7 +34,7 @@ class TestProviderLDAP(SeleniumTestCase):
|
|||
"""Start ldap container based on outpost created"""
|
||||
client: DockerClient = from_env()
|
||||
container = client.containers.run(
|
||||
image=self.get_container_image("goauthentik.io/dev-ldap"),
|
||||
image=self.get_container_image("ghcr.io/goauthentik/dev-ldap"),
|
||||
detach=True,
|
||||
network_mode="host",
|
||||
auto_remove=True,
|
||||
|
@ -183,7 +183,7 @@ class TestProviderLDAP(SeleniumTestCase):
|
|||
User.objects.filter(username="akadmin").delete()
|
||||
|
||||
_connection.search(
|
||||
"ou=users,dc=ldap,dc=goauthentik,dc=io",
|
||||
"ou=Users,DC=ldaP,dc=goauthentik,dc=io",
|
||||
"(objectClass=user)",
|
||||
search_scope=SUBTREE,
|
||||
attributes=[ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES],
|
||||
|
@ -203,8 +203,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
|||
"cn": [o_user.username],
|
||||
"sAMAccountName": [o_user.username],
|
||||
"uid": [o_user.uid],
|
||||
"name": [""],
|
||||
"displayName": [""],
|
||||
"name": [o_user.name],
|
||||
"displayName": [o_user.name],
|
||||
"mail": [""],
|
||||
"objectClass": [
|
||||
"user",
|
||||
|
@ -230,8 +230,8 @@ class TestProviderLDAP(SeleniumTestCase):
|
|||
"cn": [embedded_account.username],
|
||||
"sAMAccountName": [embedded_account.username],
|
||||
"uid": [embedded_account.uid],
|
||||
"name": [""],
|
||||
"displayName": [""],
|
||||
"name": [embedded_account.name],
|
||||
"displayName": [embedded_account.name],
|
||||
"mail": [""],
|
||||
"objectClass": [
|
||||
"user",
|
||||
|
|
|
@ -42,7 +42,7 @@ class TestProviderProxy(SeleniumTestCase):
|
|||
"""Start proxy container based on outpost created"""
|
||||
client: DockerClient = from_env()
|
||||
container = client.containers.run(
|
||||
image=self.get_container_image("goauthentik.io/dev-proxy"),
|
||||
image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"),
|
||||
detach=True,
|
||||
network_mode="host",
|
||||
environment={
|
||||
|
@ -158,8 +158,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
|
|||
sleep(0.5)
|
||||
|
||||
state = outpost.state
|
||||
self.assertEqual(len(state), 1)
|
||||
self.assertEqual(state[0].version, __version__)
|
||||
self.assertTrue(len(state) >= 1)
|
||||
|
||||
# Make sure to delete the outpost to remove the container
|
||||
outpost.delete()
|
||||
|
|
|
@ -93,10 +93,10 @@ class SeleniumTestCase(StaticLiveServerTestCase):
|
|||
def output_container_logs(self, container: Optional[Container] = None):
|
||||
"""Output the container logs to our STDOUT"""
|
||||
_container = container or self.container
|
||||
self.logger.debug("--------container logs", container=_container.image.tags[0])
|
||||
print(f"--------container logs {_container.image.tags[0]}")
|
||||
for log in _container.logs().decode().split("\n"):
|
||||
self.logger.debug(log, container=_container.image.tags[0])
|
||||
self.logger.debug("--------end container logs", container=_container.image.tags[0])
|
||||
print(log)
|
||||
print(f"--------end container logs {_container.image.tags[0]}")
|
||||
|
||||
def get_container_specs(self) -> Optional[dict[str, Any]]:
|
||||
"""Optionally get container specs which will launched on setup, wait for the container to
|
||||
|
|
|
@ -116,5 +116,5 @@ class OutpostDockerTests(ChannelsLiveServerTestCase):
|
|||
self.assertEqual(compose["version"], "3.5")
|
||||
self.assertEqual(
|
||||
compose["services"]["authentik_proxy"]["image"],
|
||||
f"goauthentik.io/dev-proxy:{get_docker_tag()}",
|
||||
f"ghcr.io/goauthentik/dev-proxy:{get_docker_tag()}",
|
||||
)
|
||||
|
|
|
@ -116,5 +116,5 @@ class TestProxyDocker(ChannelsLiveServerTestCase):
|
|||
self.assertEqual(compose["version"], "3.5")
|
||||
self.assertEqual(
|
||||
compose["services"]["authentik_proxy"]["image"],
|
||||
f"goauthentik.io/dev-proxy:{get_docker_tag()}",
|
||||
f"ghcr.io/goauthentik/dev-proxy:{get_docker_tag()}",
|
||||
)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"@babel/preset-env": "^7.16.4",
|
||||
"@babel/preset-typescript": "^7.16.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@goauthentik/api": "^2021.10.4-1638781871",
|
||||
"@goauthentik/api": "^2021.10.4-1639076050",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.3.0",
|
||||
"@lingui/cli": "^3.13.0",
|
||||
"@lingui/core": "^3.13.0",
|
||||
|
@ -29,8 +29,8 @@
|
|||
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@sentry/browser": "^6.16.0",
|
||||
"@sentry/tracing": "^6.16.0",
|
||||
"@sentry/browser": "^6.16.1",
|
||||
"@sentry/tracing": "^6.16.1",
|
||||
"@squoosh/cli": "^0.7.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.1.1",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
|
@ -44,7 +44,7 @@
|
|||
"chart.js": "^3.6.2",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"codemirror": "^5.64.0",
|
||||
"construct-style-sheets-polyfill": "^2.4.16",
|
||||
"construct-style-sheets-polyfill": "^3.0.5",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.4",
|
||||
|
@ -55,7 +55,7 @@
|
|||
"moment": "^2.29.1",
|
||||
"prettier": "^2.5.1",
|
||||
"rapidoc": "^9.1.3",
|
||||
"rollup": "^2.60.2",
|
||||
"rollup": "^2.61.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.2",
|
||||
"rollup-plugin-minify-html-literals": "^1.2.6",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.2",
|
||||
"typescript": "^4.5.3",
|
||||
"webcomponent-qr-code": "^1.0.5",
|
||||
"yaml": "^1.10.2"
|
||||
}
|
||||
|
@ -1708,9 +1708,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2021.10.4-1638781871",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.10.4-1638781871.tgz",
|
||||
"integrity": "sha512-QI/pqVVCt/W+iXZGXXipAYX39CBpuUEPDxFKPCiQyU+G0+rLYt1T1umBjTIlIEXRKC8xKSInLkDS/GEz32kXNA=="
|
||||
"version": "2021.10.4-1639076050",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.10.4-1639076050.tgz",
|
||||
"integrity": "sha512-Ud8hYHeaz9BCpJwYlOGce41MqhHeHiGVh7nlsWdUIOGCyrraJD3lFaPBgnG1l0M2RpxUeHz2owSUetS23X164g=="
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.9.2",
|
||||
|
@ -2370,13 +2370,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.0.tgz",
|
||||
"integrity": "sha512-rpFrS/DPKH9NAWfEhrgpVmqJtfUIGvl9y6KQv0QsNv7X0ZISNtsoHIUe2jVrbjysjWXrJCryCxcSxNgqsa4Www==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz",
|
||||
"integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/core": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2389,14 +2389,14 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.0.tgz",
|
||||
"integrity": "sha512-XqIlMjefuJmwQSAzv9J1PtV6+sXiz1dgBbtRr6e+QGIYZ+BDkuyDQv/HsGPfxxMHxgJBxBzi71FFLjEJsF6CBg==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz",
|
||||
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/minimal": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2409,12 +2409,12 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/hub": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.0.tgz",
|
||||
"integrity": "sha512-NBkcgGjnYsoXyIJwi2TGCxGnxbDJc/t++0ukFoBRy6RL/pw2YnryCu8PWNFsDkZdlb1zt5SIC6Kui+q1ViNS/A==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz",
|
||||
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2427,12 +2427,12 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/minimal": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.0.tgz",
|
||||
"integrity": "sha512-9/h0J9BDDY5W/dKILGEq3ewECspNoxcXuly/WOWQdt2SQpIcoh8l/dF8iTXle+icndin0EiMEyHOzaCPWG24oQ==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz",
|
||||
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2445,14 +2445,14 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.0.tgz",
|
||||
"integrity": "sha512-vTTjGnLc9fa3jM0RKkEgOLW23CiPb1Kh6bkHbUw68d3DVz6o0Tj2SqzW+Y+LaIwlFjhrozf+YV/KS9vj4BhHTw==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz",
|
||||
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/minimal": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2465,19 +2465,19 @@
|
|||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.0.tgz",
|
||||
"integrity": "sha512-ZgIyLYlQS4SPi+d68XD8n9FzoObrNQLWxBuMYMnG3uJSuFeYAJrVYkDRtW4OW0D3awuajYGiHJZC2O5qTRGflA==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz",
|
||||
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.0.tgz",
|
||||
"integrity": "sha512-FJl1AyUVAIzxfEXufWsgX7KxIvOrQawxhAhLXO4vU5xrFrJOteicxAIFJO+GG0QDELgr9siP0Qgeb8LoINWcrw==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz",
|
||||
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/types": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -3750,9 +3750,12 @@
|
|||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"node_modules/construct-style-sheets-polyfill": {
|
||||
"version": "2.4.17",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.17.tgz",
|
||||
"integrity": "sha512-rKtZGWWAVFE6HgdBuuui1emic/t8aAKQbePQ7Je6ird8nZYSd3mqqBX9IvFn2CMtnbh7mQTk/vxc9mfaLl7cGQ=="
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.0.5.tgz",
|
||||
"integrity": "sha512-prLKSx9gYwtmqWtq+pZxppU1SaH9o4ug7JIc0I/1zMV2bFE3GvRtQaMTIpotlhw33XjtC7rGQFOZJsOFnlAAhQ==",
|
||||
"engines": {
|
||||
"npm": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "1.8.0",
|
||||
|
@ -7188,9 +7191,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.60.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz",
|
||||
"integrity": "sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw==",
|
||||
"version": "2.61.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.61.1.tgz",
|
||||
"integrity": "sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA==",
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
@ -8328,9 +8331,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz",
|
||||
"integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
@ -9895,9 +9898,9 @@
|
|||
"integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg=="
|
||||
},
|
||||
"@goauthentik/api": {
|
||||
"version": "2021.10.4-1638781871",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.10.4-1638781871.tgz",
|
||||
"integrity": "sha512-QI/pqVVCt/W+iXZGXXipAYX39CBpuUEPDxFKPCiQyU+G0+rLYt1T1umBjTIlIEXRKC8xKSInLkDS/GEz32kXNA=="
|
||||
"version": "2021.10.4-1639076050",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2021.10.4-1639076050.tgz",
|
||||
"integrity": "sha512-Ud8hYHeaz9BCpJwYlOGce41MqhHeHiGVh7nlsWdUIOGCyrraJD3lFaPBgnG1l0M2RpxUeHz2owSUetS23X164g=="
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.9.2",
|
||||
|
@ -10415,13 +10418,13 @@
|
|||
}
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.0.tgz",
|
||||
"integrity": "sha512-rpFrS/DPKH9NAWfEhrgpVmqJtfUIGvl9y6KQv0QsNv7X0ZISNtsoHIUe2jVrbjysjWXrJCryCxcSxNgqsa4Www==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.16.1.tgz",
|
||||
"integrity": "sha512-F2I5RL7RTLQF9CccMrqt73GRdK3FdqaChED3RulGQX5lH6U3exHGFxwyZxSrY4x6FedfBFYlfXWWCJXpLnFkow==",
|
||||
"requires": {
|
||||
"@sentry/core": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/core": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -10433,14 +10436,14 @@
|
|||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.0.tgz",
|
||||
"integrity": "sha512-XqIlMjefuJmwQSAzv9J1PtV6+sXiz1dgBbtRr6e+QGIYZ+BDkuyDQv/HsGPfxxMHxgJBxBzi71FFLjEJsF6CBg==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.16.1.tgz",
|
||||
"integrity": "sha512-UFI0264CPUc5cR1zJH+S2UPOANpm6dLJOnsvnIGTjsrwzR0h8Hdl6rC2R/GPq+WNbnipo9hkiIwDlqbqvIU5vw==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/minimal": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -10452,12 +10455,12 @@
|
|||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.0.tgz",
|
||||
"integrity": "sha512-NBkcgGjnYsoXyIJwi2TGCxGnxbDJc/t++0ukFoBRy6RL/pw2YnryCu8PWNFsDkZdlb1zt5SIC6Kui+q1ViNS/A==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.16.1.tgz",
|
||||
"integrity": "sha512-4PGtg6AfpqMkreTpL7ymDeQ/U1uXv03bKUuFdtsSTn/FRf9TLS4JB0KuTZCxfp1IRgAA+iFg6B784dDkT8R9eg==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -10469,12 +10472,12 @@
|
|||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.0.tgz",
|
||||
"integrity": "sha512-9/h0J9BDDY5W/dKILGEq3ewECspNoxcXuly/WOWQdt2SQpIcoh8l/dF8iTXle+icndin0EiMEyHOzaCPWG24oQ==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.16.1.tgz",
|
||||
"integrity": "sha512-dq+mI1EQIvUM+zJtGCVgH3/B3Sbx4hKlGf2Usovm9KoqWYA+QpfVBholYDe/H2RXgO7LFEefDLvOdHDkqeJoyA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -10486,14 +10489,14 @@
|
|||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.0.tgz",
|
||||
"integrity": "sha512-vTTjGnLc9fa3jM0RKkEgOLW23CiPb1Kh6bkHbUw68d3DVz6o0Tj2SqzW+Y+LaIwlFjhrozf+YV/KS9vj4BhHTw==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.16.1.tgz",
|
||||
"integrity": "sha512-MPSbqXX59P+OEeST+U2V/8Hu/8QjpTUxTNeNyTHWIbbchdcMMjDbXTS3etCgajZR6Ro+DHElOz5cdSxH6IBGlA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.16.0",
|
||||
"@sentry/minimal": "6.16.0",
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/utils": "6.16.0",
|
||||
"@sentry/hub": "6.16.1",
|
||||
"@sentry/minimal": "6.16.1",
|
||||
"@sentry/types": "6.16.1",
|
||||
"@sentry/utils": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -10505,16 +10508,16 @@
|
|||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.0.tgz",
|
||||
"integrity": "sha512-ZgIyLYlQS4SPi+d68XD8n9FzoObrNQLWxBuMYMnG3uJSuFeYAJrVYkDRtW4OW0D3awuajYGiHJZC2O5qTRGflA=="
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.16.1.tgz",
|
||||
"integrity": "sha512-Wh354g30UsJ5kYJbercektGX4ZMc9MHU++1NjeN2bTMnbofEcpUDWIiKeulZEY65IC1iU+1zRQQgtYO+/hgCUQ=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.0.tgz",
|
||||
"integrity": "sha512-FJl1AyUVAIzxfEXufWsgX7KxIvOrQawxhAhLXO4vU5xrFrJOteicxAIFJO+GG0QDELgr9siP0Qgeb8LoINWcrw==",
|
||||
"version": "6.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.16.1.tgz",
|
||||
"integrity": "sha512-7ngq/i4R8JZitJo9Sl8PDnjSbDehOxgr1vsoMmerIsyRZ651C/8B+jVkMhaAPgSdyJ0AlE3O7DKKTP1FXFw9qw==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.16.0",
|
||||
"@sentry/types": "6.16.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -11457,9 +11460,9 @@
|
|||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"construct-style-sheets-polyfill": {
|
||||
"version": "2.4.17",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.17.tgz",
|
||||
"integrity": "sha512-rKtZGWWAVFE6HgdBuuui1emic/t8aAKQbePQ7Je6ird8nZYSd3mqqBX9IvFn2CMtnbh7mQTk/vxc9mfaLl7cGQ=="
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.0.5.tgz",
|
||||
"integrity": "sha512-prLKSx9gYwtmqWtq+pZxppU1SaH9o4ug7JIc0I/1zMV2bFE3GvRtQaMTIpotlhw33XjtC7rGQFOZJsOFnlAAhQ=="
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "1.8.0",
|
||||
|
@ -14025,9 +14028,9 @@
|
|||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.60.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.2.tgz",
|
||||
"integrity": "sha512-1Bgjpq61sPjgoZzuiDSGvbI1tD91giZABgjCQBKM5aYLnzjq52GoDuWVwT/cm/MCxCMPU8gqQvkj8doQ5C8Oqw==",
|
||||
"version": "2.61.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.61.1.tgz",
|
||||
"integrity": "sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA==",
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
|
@ -14913,9 +14916,9 @@
|
|||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw=="
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.3.tgz",
|
||||
"integrity": "sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ=="
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.14.1",
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"@babel/preset-env": "^7.16.4",
|
||||
"@babel/preset-typescript": "^7.16.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@goauthentik/api": "^2021.10.4-1638781871",
|
||||
"@goauthentik/api": "^2021.10.4-1639076050",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.3.0",
|
||||
"@lingui/cli": "^3.13.0",
|
||||
"@lingui/core": "^3.13.0",
|
||||
|
@ -65,8 +65,8 @@
|
|||
"@rollup/plugin-node-resolve": "^13.0.6",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@sentry/browser": "^6.16.0",
|
||||
"@sentry/tracing": "^6.16.0",
|
||||
"@sentry/browser": "^6.16.1",
|
||||
"@sentry/tracing": "^6.16.1",
|
||||
"@squoosh/cli": "^0.7.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.1.1",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
|
@ -80,7 +80,7 @@
|
|||
"chart.js": "^3.6.2",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"codemirror": "^5.64.0",
|
||||
"construct-style-sheets-polyfill": "^2.4.16",
|
||||
"construct-style-sheets-polyfill": "^3.0.5",
|
||||
"eslint": "^8.4.1",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.4",
|
||||
|
@ -91,7 +91,7 @@
|
|||
"moment": "^2.29.1",
|
||||
"prettier": "^2.5.1",
|
||||
"rapidoc": "^9.1.3",
|
||||
"rollup": "^2.60.2",
|
||||
"rollup": "^2.61.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.2",
|
||||
"rollup-plugin-minify-html-literals": "^1.2.6",
|
||||
|
@ -99,7 +99,7 @@
|
|||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.2",
|
||||
"typescript": "^4.5.3",
|
||||
"webcomponent-qr-code": "^1.0.5",
|
||||
"yaml": "^1.10.2"
|
||||
}
|
||||
|
|
|
@ -338,6 +338,9 @@ html > form > input {
|
|||
.pf-c-notification-drawer__list-item {
|
||||
background-color: var(--ak-dark-background-light-ish);
|
||||
color: var(--ak-dark-foreground);
|
||||
--pf-c-notification-drawer__list-item--BorderBottomColor: var(
|
||||
--ak-dark-background-lighter
|
||||
) !important;
|
||||
}
|
||||
/* data list */
|
||||
.pf-c-data-list__item {
|
||||
|
|
|
@ -62,7 +62,7 @@ export class UserListPage extends TablePage<User> {
|
|||
search: this.search || "",
|
||||
attributes: this.hideServiceAccounts
|
||||
? JSON.stringify({
|
||||
"goauthentik.io/user/service-account__isnull": "true",
|
||||
"goauthentik.io/user/service-account__isnull": true,
|
||||
})
|
||||
: undefined,
|
||||
});
|
||||
|
|
|
@ -21,7 +21,6 @@ If you want to only make changes on the UI, you don't need a backend running fro
|
|||
AUTHENTIK_TAG=gh-next
|
||||
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=goauthentik.io/dev-%(type)s:gh-next
|
||||
AUTHENTIK_LOG_LEVEL=debug
|
||||
AUTHENTIK_WEB_LOAD_LOCAL_FILES=true
|
||||
```
|
||||
|
||||
This will cause authentik to use the beta images.
|
||||
|
|
|
@ -7,31 +7,16 @@ services:
|
|||
container_name: traefik
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.api.rule: Host(`traefik.example.com`)
|
||||
traefik.http.routers.api.entrypoints: https
|
||||
traefik.http.routers.api.service: api@internal
|
||||
traefik.http.routers.api.tls: true
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
command:
|
||||
- '--api'
|
||||
- '--log=true'
|
||||
- '--log.level=DEBUG'
|
||||
- '--log.filepath=/var/log/traefik.log'
|
||||
- '--providers.docker=true'
|
||||
- '--providers.docker.exposedByDefault=false'
|
||||
- '--entrypoints.http=true'
|
||||
- '--entrypoints.http.address=:80'
|
||||
- '--entrypoints.http.http.redirections.entrypoint.to=https'
|
||||
- '--entrypoints.http.http.redirections.entrypoint.scheme=https'
|
||||
- '--entrypoints.https=true'
|
||||
- '--entrypoints.https.address=:443'
|
||||
- "--entrypoints.web.address=:80"
|
||||
|
||||
authentik_proxy:
|
||||
image: goauthentik.io/proxy:2021.5.1
|
||||
authentik-proxy:
|
||||
image: goauthentik.io/proxy:latest
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9443:9443
|
||||
|
@ -46,11 +31,10 @@ services:
|
|||
traefik.enable: true
|
||||
traefik.port: 9000
|
||||
traefik.http.routers.authentik.rule: Host(`app.company`) && PathPrefix(`/akprox/`)
|
||||
traefik.http.routers.authentik.entrypoints: https
|
||||
traefik.http.routers.authentik.tls: true
|
||||
traefik.http.middlewares.authentik.forwardauth.address: http://outpost.company:9000/akprox/auth/traefik
|
||||
# `authentik-proxy` refers to the service name in the compose file.
|
||||
traefik.http.middlewares.authentik.forwardauth.address: http://authentik-proxy:9000/akprox/auth/traefik
|
||||
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: true
|
||||
traefik.http.middlewares.authentik.forwardauth.authResponseHeadersRegex: ^.*$
|
||||
traefik.http.middlewares.authentik.forwardauth.authResponseHeadersRegex: ^.*$$
|
||||
restart: unless-stopped
|
||||
|
||||
whoami:
|
||||
|
@ -58,8 +42,6 @@ services:
|
|||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.whoami.rule: Host(`app.company`)
|
||||
traefik.http.routers.whoami.entrypoints: https
|
||||
traefik.http.routers.whoami.tls: true
|
||||
traefik.http.routers.whoami.middlewares: authentik@docker
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
|
|
@ -86,6 +86,23 @@ This release does not have any headline features, and mostly fixes bugs.
|
|||
- web/flows: fix linting errors
|
||||
- web/flows: Revise duo authenticator login prompt text (#1872)
|
||||
|
||||
## Fixed in 2021.12.1-rc3
|
||||
|
||||
- core: add FlowToken which saves the pickled flow plan, replace standard token in email stage to allow finishing flows in different sessions
|
||||
- core: fix missing permission check for group creating when creating service account
|
||||
- outposts/ldap: Fix search case sensitivity. (#1897)
|
||||
- policies/expression: add ak_call_policy
|
||||
- providers/saml: add ?force_binding to limit bindings for metadata endpoint
|
||||
- root: add request_id to celery tasks, prefixed with "task-"
|
||||
- sources/*: Allow creation of source connections via API
|
||||
- stages/prompt: use policyenginemode all
|
||||
- tests/e2e: add post binding test
|
||||
- web: fix duplicate classes, make generic icon clickable
|
||||
- web: fix text colour for bad request on light mode
|
||||
- web/admin: show outpost warning on application page too
|
||||
- web/elements: close dropdown when refresh event is dispatched
|
||||
- web/user: allow custom font-awesome icons for applications
|
||||
|
||||
## Upgrading
|
||||
|
||||
This release does not introduce any new requirements.
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
title: FortiManager
|
||||
---
|
||||
|
||||
## What is FortiManager
|
||||
|
||||
From https://www.fortinet.com/products/management/fortimanager
|
||||
|
||||
:::note
|
||||
FortiManager supports network operations use cases for centralized management, best practices compliance, and workflow automation to provide better protection against breaches.
|
||||
|
||||
FortiManager is a paid enterprise product.
|
||||
:::
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `fgm.company` is the FQDN of the FortiManager install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an application and Provider in authentik, note the slug, as this will be used later. Create a SAML provider with the following parameters:
|
||||
|
||||
Provider:
|
||||
- ACS URL: `https://fgm.company/saml/?acs`
|
||||
- Issuer: `https://authentik.company/application/saml/fgm/sso/binding/redirect/`
|
||||
- Service Provider Binding: Post
|
||||
|
||||
You can of course use a custom signing certificate, and adjust durations.
|
||||
|
||||
Application:
|
||||
- Launch URL: 'https://fgm.company/p/sso_sp/'
|
||||
|
||||
## FortiManager Configuration
|
||||
|
||||
Navigate to `https://fgm.company/p/app/#!/sys/sso_settings` and select SAML SSO settings to configure SAML.
|
||||
|
||||
Select 'Service Provider (SP)' under Single Sign-On Mode to enable SAML authentication.
|
||||
|
||||
Set the Field 'SP Address' to the FortiManager FQDN 'fgm.company'. (This gives you the URLs to configure in Authentik)
|
||||
|
||||
Set the Default Login Page to either 'Normal' or 'Single-Sign On'. (Normal allows both local and SAML authentication vs only SAML SSO)
|
||||
|
||||
FortiManager create a new user by default if one does not exist so you will need to set the Default Admin Profile to the permissions you want any new users to have. (We created a no_permissions profile to assign by default)
|
||||
|
||||
Set the Field 'IdP Type' to 'Custom'
|
||||
|
||||
Set the Field `IdP entity ID` to `https://authentik.company/application/saml/fgm/sso/binding/redirect/`.
|
||||
|
||||
Set the Field `IdP Login URL` to `https://authentik.company/application/saml/fgm/sso/binding/redirect/`.
|
||||
|
||||
Set the Field `IdP Logout URL` to `https://authentik.company/`
|
||||
|
||||
For the Field 'IdP Certificate" Import your Authentik cert. (Self Signed or real)
|
File diff suppressed because it is too large
Load Diff
|
@ -12,11 +12,11 @@
|
|||
"serve": "docusaurus serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.9",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.9",
|
||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.13",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.13",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.1.1",
|
||||
"postcss": "^8.4.4",
|
||||
"postcss": "^8.4.5",
|
||||
"rapidoc": "^9.1.3",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^1.1.1",
|
||||
|
|
|
@ -31,6 +31,7 @@ module.exports = {
|
|||
"services/awx-tower/index",
|
||||
"services/bookstack/index",
|
||||
"services/budibase/index",
|
||||
"services/fortimanager/index",
|
||||
"services/gitea/index",
|
||||
"services/gitlab/index",
|
||||
"services/grafana/index",
|
||||
|
|
Reference in New Issue