Compare commits
138 Commits
version/20
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
58c221e867 | |||
108d3e56e3 | |||
145b32c480 | |||
c788504bb0 | |||
34782b31e5 | |||
5a3ca13d76 | |||
5dc0f3b91b | |||
f51515f3de | |||
f978575293 | |||
cb64eed90d | |||
db1f7f0400 | |||
0d02dbf55c | |||
6da78b8c32 | |||
3a80bc8bda | |||
1aa9c0f9ca | |||
2da7a8fede | |||
89cb402f42 | |||
b617fd213f | |||
97b0f58f25 | |||
49a98bb744 | |||
f93a00d773 | |||
8de40a8a21 | |||
b9c54e97fa | |||
f1c55465f7 | |||
40c2b2860b | |||
a92bce322d | |||
af83308fd4 | |||
73d991e75a | |||
1eba3f1334 | |||
b86251255d | |||
ccab41a6ca | |||
0e051031b1 | |||
aecbe8c585 | |||
da98022704 | |||
e13f9c0b38 | |||
7941fb9d95 | |||
d2392b0881 | |||
b2044d75fb | |||
617b64b7db | |||
2bf5f2709a | |||
f03325df28 | |||
2b71e5bdfd | |||
f861737b85 | |||
6036d88392 | |||
bfc8a56a0b | |||
8d995011b8 | |||
5646141fe2 | |||
96b0bc324e | |||
335d6edd11 | |||
5d9bed130a | |||
0a1ab74707 | |||
ef24b94585 | |||
77b0438aa4 | |||
2788329880 | |||
15ab11be70 | |||
8d5460a132 | |||
5ba2c80813 | |||
06766bdb25 | |||
fdae13316c | |||
ae21886e8e | |||
f5dc81907a | |||
40f8ce3c4c | |||
c934915776 | |||
d70c8fbcc3 | |||
12b26e49ec | |||
0ac548d56e | |||
e771e1857f | |||
479e9750c7 | |||
c5e7801247 | |||
48ea15a946 | |||
e4c06f7356 | |||
4d7d866e4b | |||
72a93c0959 | |||
73733b20b6 | |||
3872314931 | |||
85c6ede448 | |||
49c2bee9d6 | |||
6b2c9d7c44 | |||
381010600f | |||
2a265f706a | |||
1b21b50b77 | |||
fa6324ab1d | |||
9e0daf2bcf | |||
0273ae16df | |||
f2f12ef0ba | |||
61d3df5f02 | |||
971de4fcb9 | |||
9c0bc78ca0 | |||
92085f1a3c | |||
6067406e96 | |||
9ccd4d69fe | |||
17ec48332d | |||
d3f5253a6b | |||
7a70726d57 | |||
be303937fb | |||
2326fc9ae2 | |||
9374b0bcf2 | |||
47e6028099 | |||
24114e8304 | |||
921d9c79a1 | |||
1119989ab7 | |||
e17594f0f7 | |||
5ae3b868d4 | |||
37ee4af5ff | |||
829aaca317 | |||
8eb4d53810 | |||
e60dfc5b3c | |||
cc403d8777 | |||
b81e2e69d1 | |||
731f5d0199 | |||
a40cb03b44 | |||
f6a85c98c9 | |||
5727f28784 | |||
6fc54ed7c6 | |||
4298900ecc | |||
f04aa09b72 | |||
3647633232 | |||
2e06786869 | |||
eba91c6b2b | |||
ba9f8a5795 | |||
02b4173d30 | |||
61fab497cf | |||
6a95de4e8a | |||
621e7f564a | |||
535f2eb27e | |||
0db4716e92 | |||
c10ce5c679 | |||
070438aabe | |||
71798b931c | |||
8663134c87 | |||
6bcbaeec2e | |||
17ce113c6b | |||
ff600cd5b1 | |||
2df4322ecf | |||
bb8e0c6f59 | |||
ca682c3ee4 | |||
f011e8a61a | |||
b8aff17d98 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2021.10.1-rc1
|
||||
current_version = 2021.10.2
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
|
||||
|
3
.github/workflows/ghcr-retention.yml
vendored
3
.github/workflows/ghcr-retention.yml
vendored
@ -3,6 +3,7 @@ name: ghcr-retention
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # every day at midnight
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
clean-ghcr:
|
||||
@ -13,7 +14,7 @@ jobs:
|
||||
uses: sondrelg/container-retention-policy@v1
|
||||
with:
|
||||
image-names: dev-server,dev-ldap,dev-proxy
|
||||
cut-off: One month ago UTC
|
||||
cut-off: One week ago UTC
|
||||
account-type: org
|
||||
org-name: goauthentik
|
||||
untagged-only: false
|
||||
|
20
.github/workflows/release-publish.yml
vendored
20
.github/workflows/release-publish.yml
vendored
@ -30,14 +30,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik:2021.10.1-rc1,
|
||||
beryju/authentik:2021.10.2,
|
||||
beryju/authentik:latest,
|
||||
ghcr.io/goauthentik/server:2021.10.1-rc1,
|
||||
ghcr.io/goauthentik/server:2021.10.2,
|
||||
ghcr.io/goauthentik/server:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.2', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik:latest
|
||||
docker tag beryju/authentik:latest beryju/authentik:stable
|
||||
@ -72,14 +72,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-proxy:2021.10.1-rc1,
|
||||
beryju/authentik-proxy:2021.10.2,
|
||||
beryju/authentik-proxy:latest,
|
||||
ghcr.io/goauthentik/proxy:2021.10.1-rc1,
|
||||
ghcr.io/goauthentik/proxy:2021.10.2,
|
||||
ghcr.io/goauthentik/proxy:latest
|
||||
file: proxy.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.2', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik-proxy:latest
|
||||
docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable
|
||||
@ -114,14 +114,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-ldap:2021.10.1-rc1,
|
||||
beryju/authentik-ldap:2021.10.2,
|
||||
beryju/authentik-ldap:latest,
|
||||
ghcr.io/goauthentik/ldap:2021.10.1-rc1,
|
||||
ghcr.io/goauthentik/ldap:2021.10.2,
|
||||
ghcr.io/goauthentik/ldap:latest
|
||||
file: ldap.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.10.2', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik-ldap:latest
|
||||
docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable
|
||||
@ -170,7 +170,7 @@ jobs:
|
||||
SENTRY_PROJECT: authentik
|
||||
SENTRY_URL: https://sentry.beryju.org
|
||||
with:
|
||||
version: authentik@2021.10.1-rc1
|
||||
version: authentik@2021.10.2
|
||||
environment: beryjuorg-prod
|
||||
sourcemaps: './web/dist'
|
||||
url_prefix: '~/static/dist'
|
||||
|
@ -31,7 +31,7 @@ Basically, don't be a dickhead. This is an open-source non-profit project, that
|
||||
|
||||
## I don't want to read this whole thing I just have a question!!!
|
||||
|
||||
Either [create a question on GitHub](https://github.com/goauthentik/authentik/issues/new?assignees=&labels=question&template=question.md&title=) or join [the Discord server](https://discord.gg/jg33eMhnj6)
|
||||
Either [create a question on GitHub](https://github.com/goauthentik/authentik/issues/new?assignees=&labels=question&template=question.md&title=) or join [the Discord server](https://goauthentik.io/discord)
|
||||
|
||||
## What should I know before I get started?
|
||||
|
||||
@ -131,7 +131,7 @@ When you are creating an enhancement suggestion, please fill in [the template](h
|
||||
|
||||
authentik can be run locally, all though depending on which part you want to work on, different pre-requisites are required.
|
||||
|
||||
This is documented in the [developer docs](https://goauthentik.io/developer-docs/)
|
||||
This is documented in the [developer docs](https://goauthentik.io/developer-docs/?utm_source=github)
|
||||
|
||||
### Pull Requests
|
||||
|
||||
|
12
Dockerfile
12
Dockerfile
@ -1,5 +1,5 @@
|
||||
# Stage 1: Lock python dependencies
|
||||
FROM docker.io/python:3.9-slim-buster as locker
|
||||
FROM docker.io/python:3.9-bullseye as locker
|
||||
|
||||
COPY ./Pipfile /app/
|
||||
COPY ./Pipfile.lock /app/
|
||||
@ -11,7 +11,7 @@ RUN pip install pipenv && \
|
||||
pipenv lock -r --dev-only > requirements-dev.txt
|
||||
|
||||
# Stage 2: Build website
|
||||
FROM docker.io/node as website-builder
|
||||
FROM docker.io/node:16 as website-builder
|
||||
|
||||
COPY ./website /static/
|
||||
|
||||
@ -19,7 +19,7 @@ ENV NODE_ENV=production
|
||||
RUN cd /static && npm i && npm run build-docs-only
|
||||
|
||||
# Stage 3: Build webui
|
||||
FROM docker.io/node as web-builder
|
||||
FROM docker.io/node:16 as web-builder
|
||||
|
||||
COPY ./web /static/
|
||||
|
||||
@ -27,7 +27,7 @@ ENV NODE_ENV=production
|
||||
RUN cd /static && npm i && npm run build
|
||||
|
||||
# Stage 4: Build go proxy
|
||||
FROM docker.io/golang:1.17.2 AS builder
|
||||
FROM docker.io/golang:1.17.2-bullseye AS builder
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
@ -47,7 +47,7 @@ COPY ./go.sum /work/go.sum
|
||||
RUN go build -o /work/authentik ./cmd/server/main.go
|
||||
|
||||
# Stage 5: Run
|
||||
FROM docker.io/python:3.9-slim-buster
|
||||
FROM docker.io/python:3.9-bullseye
|
||||
|
||||
WORKDIR /
|
||||
COPY --from=locker /app/requirements.txt /
|
||||
@ -59,7 +59,7 @@ ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends curl ca-certificates gnupg git runit && \
|
||||
curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt bullseye-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends libpq-dev postgresql-client build-essential libxmlsec1-dev pkg-config libmaxminddb0 && \
|
||||
pip install -r /requirements.txt --no-cache-dir && \
|
||||
|
1
Makefile
1
Makefile
@ -30,7 +30,6 @@ lint-fix:
|
||||
website/developer-docs
|
||||
|
||||
lint:
|
||||
pyright authentik tests lifecycle
|
||||
bandit -r authentik tests lifecycle -x node_modules
|
||||
pylint authentik tests lifecycle
|
||||
|
||||
|
2
Pipfile
2
Pipfile
@ -26,7 +26,7 @@ drf-spectacular = "*"
|
||||
facebook-sdk = "*"
|
||||
geoip2 = "*"
|
||||
gunicorn = "*"
|
||||
kubernetes = "==v19.15.0b1"
|
||||
kubernetes = "==v19.15.0"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
packaging = "*"
|
||||
|
864
Pipfile.lock
generated
864
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://discord.gg/jg33eMhnj6)
|
||||
[](https://goauthentik.io/discord)
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-main.yml)
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-outpost.yml)
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-web.yml)
|
||||
@ -20,9 +20,9 @@ authentik is an open-source Identity Provider focused on flexibility and versati
|
||||
|
||||
## Installation
|
||||
|
||||
For small/test setups it is recommended to use docker-compose, see the [documentation](https://goauthentik.io/docs/installation/docker-compose/)
|
||||
For small/test setups it is recommended to use docker-compose, see the [documentation](https://goauthentik.io/docs/installation/docker-compose/?utm_source=github)
|
||||
|
||||
For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/)
|
||||
For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/?utm_source=github)
|
||||
|
||||
## Screenshots
|
||||
|
||||
@ -33,7 +33,7 @@ Light | Dark
|
||||
|
||||
## Development
|
||||
|
||||
See [Development Documentation](https://goauthentik.io/developer-docs/)
|
||||
See [Development Documentation](https://goauthentik.io/developer-docs/?utm_source=github)
|
||||
|
||||
## Security
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
"""authentik"""
|
||||
__version__ = "2021.10.1-rc1"
|
||||
__version__ = "2021.10.2"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
@ -9,6 +9,7 @@ from rest_framework.exceptions import AuthenticationFailed
|
||||
from rest_framework.request import Request
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import KEY_AUTH_VIA, LOCAL
|
||||
from authentik.core.models import Token, TokenIntents, User
|
||||
from authentik.outposts.models import Outpost
|
||||
|
||||
@ -44,6 +45,8 @@ def bearer_auth(raw_header: bytes) -> Optional[User]:
|
||||
if not user:
|
||||
raise AuthenticationFailed("Token invalid/expired")
|
||||
return user
|
||||
if hasattr(LOCAL, "authentik"):
|
||||
LOCAL.authentik[KEY_AUTH_VIA] = "api_token"
|
||||
return tokens.first().user
|
||||
|
||||
|
||||
@ -57,7 +60,8 @@ def token_secret_key(value: str) -> Optional[User]:
|
||||
outposts = Outpost.objects.filter(managed=MANAGED_OUTPOST)
|
||||
if not outposts:
|
||||
return None
|
||||
LOGGER.info("Authenticating via secret_key")
|
||||
if hasattr(LOCAL, "authentik"):
|
||||
LOCAL.authentik[KEY_AUTH_VIA] = "secret_key"
|
||||
outpost = outposts.first()
|
||||
return outpost.user
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
"""API tasks"""
|
||||
|
||||
from authentik.lib.utils.http import get_http_session
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
SENTRY_SESSION = get_http_session()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
def sentry_proxy(payload: str):
|
||||
"""Relay data to sentry"""
|
||||
SENTRY_SESSION.post(
|
||||
"https://sentry.beryju.org/api/8/envelope/",
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
timeout=10,
|
||||
)
|
@ -1,65 +0,0 @@
|
||||
"""Sentry tunnel"""
|
||||
from json import loads
|
||||
|
||||
from django.conf import settings
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.parsers import BaseParser
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.throttling import AnonRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.api.tasks import sentry_proxy
|
||||
from authentik.lib.config import CONFIG
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class PlainTextParser(BaseParser):
|
||||
"""Plain text parser."""
|
||||
|
||||
media_type = "text/plain"
|
||||
|
||||
def parse(self, stream, media_type=None, parser_context=None) -> str:
|
||||
"""Simply return a string representing the body of the request."""
|
||||
return stream.read()
|
||||
|
||||
|
||||
class CsrfExemptSessionAuthentication(SessionAuthentication):
|
||||
"""CSRF-exempt Session authentication"""
|
||||
|
||||
def enforce_csrf(self, request: Request):
|
||||
return # To not perform the csrf check previously happening
|
||||
|
||||
|
||||
class SentryTunnelView(APIView):
|
||||
"""Sentry tunnel, to prevent ad blockers from blocking sentry"""
|
||||
|
||||
serializer_class = None
|
||||
parser_classes = [PlainTextParser]
|
||||
throttle_classes = [AnonRateThrottle]
|
||||
permission_classes = [AllowAny]
|
||||
authentication_classes = [CsrfExemptSessionAuthentication]
|
||||
|
||||
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):
|
||||
LOGGER.debug("error reporting disabled")
|
||||
return HttpResponse(status=400)
|
||||
# Body is 2 json objects separated by \n
|
||||
full_body = request.body
|
||||
lines = full_body.splitlines()
|
||||
if len(lines) < 1:
|
||||
return HttpResponse(status=400)
|
||||
header = loads(lines[0])
|
||||
# Check that the DSN is what we expect
|
||||
dsn = header.get("dsn", "")
|
||||
if dsn != settings.SENTRY_DSN:
|
||||
LOGGER.debug("Invalid dsn", have=dsn, expected=settings.SENTRY_DSN)
|
||||
return HttpResponse(status=400)
|
||||
sentry_proxy.delay(full_body.decode())
|
||||
return HttpResponse(status=204)
|
@ -11,14 +11,14 @@ from authentik.admin.api.tasks import TaskViewSet
|
||||
from authentik.admin.api.version import VersionView
|
||||
from authentik.admin.api.workers import WorkerView
|
||||
from authentik.api.v3.config import ConfigView
|
||||
from authentik.api.v3.sentry import SentryTunnelView
|
||||
from authentik.api.views import APIBrowserView
|
||||
from authentik.core.api.applications import ApplicationViewSet
|
||||
from authentik.core.api.authenticated_sessions import AuthenticatedSessionViewSet
|
||||
from authentik.core.api.devices import DeviceViewSet
|
||||
from authentik.core.api.groups import GroupViewSet
|
||||
from authentik.core.api.propertymappings import PropertyMappingViewSet
|
||||
from authentik.core.api.providers import ProviderViewSet
|
||||
from authentik.core.api.sources import SourceViewSet
|
||||
from authentik.core.api.sources import SourceViewSet, UserSourceConnectionViewSet
|
||||
from authentik.core.api.tokens import TokenViewSet
|
||||
from authentik.core.api.users import UserViewSet
|
||||
from authentik.crypto.api import CertificateKeyPairViewSet
|
||||
@ -136,6 +136,7 @@ router.register("events/transports", NotificationTransportViewSet)
|
||||
router.register("events/rules", NotificationRuleViewSet)
|
||||
|
||||
router.register("sources/all", SourceViewSet)
|
||||
router.register("sources/user_connections/all", UserSourceConnectionViewSet)
|
||||
router.register("sources/user_connections/oauth", UserOAuthSourceConnectionViewSet)
|
||||
router.register("sources/user_connections/plex", PlexSourceConnectionViewSet)
|
||||
router.register("sources/ldap", LDAPSourceViewSet)
|
||||
@ -169,6 +170,7 @@ router.register("propertymappings/saml", SAMLPropertyMappingViewSet)
|
||||
router.register("propertymappings/scope", ScopeMappingViewSet)
|
||||
router.register("propertymappings/notification", NotificationWebhookMappingViewSet)
|
||||
|
||||
router.register("authenticators/all", DeviceViewSet, basename="device")
|
||||
router.register("authenticators/duo", DuoDeviceViewSet)
|
||||
router.register("authenticators/sms", SMSDeviceViewSet)
|
||||
router.register("authenticators/static", StaticDeviceViewSet)
|
||||
@ -246,7 +248,6 @@ urlpatterns = (
|
||||
FlowInspectorView.as_view(),
|
||||
name="flow-inspector",
|
||||
),
|
||||
path("sentry/", SentryTunnelView.as_view(), name="sentry"),
|
||||
path("schema/", cache_page(86400)(SpectacularAPIView.as_view()), name="schema"),
|
||||
]
|
||||
)
|
||||
|
36
authentik/core/api/devices.py
Normal file
36
authentik/core/api/devices.py
Normal file
@ -0,0 +1,36 @@
|
||||
"""Authenticator Devices API Views"""
|
||||
from django_otp import devices_for_user
|
||||
from django_otp.models import Device
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.fields import CharField, IntegerField, SerializerMethodField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.core.api.utils import MetaNameSerializer
|
||||
|
||||
|
||||
class DeviceSerializer(MetaNameSerializer):
|
||||
"""Serializer for Duo authenticator devices"""
|
||||
|
||||
pk = IntegerField()
|
||||
name = CharField()
|
||||
type = SerializerMethodField()
|
||||
|
||||
def get_type(self, instance: Device) -> str:
|
||||
"""Get type of device"""
|
||||
return instance._meta.label
|
||||
|
||||
|
||||
class DeviceViewSet(ViewSet):
|
||||
"""Viewset for authenticator devices"""
|
||||
|
||||
serializer_class = DeviceSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses={200: DeviceSerializer(many=True)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Get all devices for current user"""
|
||||
devices = devices_for_user(request.user)
|
||||
return Response(DeviceSerializer(devices, many=True).data)
|
@ -1,18 +1,21 @@
|
||||
"""Source API Views"""
|
||||
from typing import Iterable
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer, SerializerMethodField
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.models import Source
|
||||
from authentik.core.models import Source, UserSourceConnection
|
||||
from authentik.core.types import UserSettingSerializer
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
@ -113,3 +116,39 @@ class SourceViewSet(
|
||||
LOGGER.warning(source_settings.errors)
|
||||
matching_sources.append(source_settings.validated_data)
|
||||
return Response(matching_sources)
|
||||
|
||||
|
||||
class UserSourceConnectionSerializer(SourceSerializer):
|
||||
"""OAuth Source Serializer"""
|
||||
|
||||
source = SourceSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = UserSourceConnection
|
||||
fields = [
|
||||
"pk",
|
||||
"user",
|
||||
"source",
|
||||
"created",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"user": {"read_only": True},
|
||||
"created": {"read_only": True},
|
||||
}
|
||||
|
||||
|
||||
class UserSourceConnectionViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
UsedByMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
"""User-source connection Viewset"""
|
||||
|
||||
queryset = UserSourceConnection.objects.all()
|
||||
serializer_class = UserSourceConnectionSerializer
|
||||
permission_classes = [OwnerPermissions]
|
||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
ordering = ["pk"]
|
||||
|
@ -45,6 +45,8 @@ from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
|
||||
from authentik.core.middleware import SESSION_IMPERSONATE_ORIGINAL_USER, SESSION_IMPERSONATE_USER
|
||||
from authentik.core.models import (
|
||||
USER_ATTRIBUTE_CHANGE_EMAIL,
|
||||
USER_ATTRIBUTE_CHANGE_USERNAME,
|
||||
USER_ATTRIBUTE_SA,
|
||||
USER_ATTRIBUTE_TOKEN_EXPIRING,
|
||||
Group,
|
||||
@ -113,14 +115,30 @@ class UserSelfSerializer(ModelSerializer):
|
||||
)
|
||||
)
|
||||
)
|
||||
def get_groups(self, user: User):
|
||||
def get_groups(self, _: User):
|
||||
"""Return only the group names a user is member of"""
|
||||
for group in user.ak_groups.all():
|
||||
for group in self.instance.ak_groups.all():
|
||||
yield {
|
||||
"name": group.name,
|
||||
"pk": group.pk,
|
||||
}
|
||||
|
||||
def validate_email(self, email: str):
|
||||
"""Check if the user is allowed to change their email"""
|
||||
if self.instance.group_attributes().get(USER_ATTRIBUTE_CHANGE_EMAIL, True):
|
||||
return email
|
||||
if email != self.instance.email:
|
||||
raise ValidationError("Not allowed to change email.")
|
||||
return email
|
||||
|
||||
def validate_username(self, username: str):
|
||||
"""Check if the user is allowed to change their username"""
|
||||
if self.instance.group_attributes().get(USER_ATTRIBUTE_CHANGE_USERNAME, True):
|
||||
return username
|
||||
if username != self.instance.username:
|
||||
raise ValidationError("Not allowed to change username.")
|
||||
return username
|
||||
|
||||
class Meta:
|
||||
|
||||
model = User
|
||||
@ -311,13 +329,14 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
# pylint: disable=invalid-name
|
||||
def me(self, request: Request) -> Response:
|
||||
"""Get information about current user"""
|
||||
serializer = SessionUserSerializer(data={"user": UserSelfSerializer(request.user).data})
|
||||
serializer = SessionUserSerializer(
|
||||
data={"user": UserSelfSerializer(instance=request.user).data}
|
||||
)
|
||||
if SESSION_IMPERSONATE_USER in request._request.session:
|
||||
serializer.initial_data["original"] = UserSelfSerializer(
|
||||
request._request.session[SESSION_IMPERSONATE_ORIGINAL_USER]
|
||||
instance=request._request.session[SESSION_IMPERSONATE_ORIGINAL_USER]
|
||||
).data
|
||||
serializer.is_valid()
|
||||
return Response(serializer.data)
|
||||
return Response(serializer.initial_data)
|
||||
|
||||
@extend_schema(request=UserSelfSerializer, responses={200: SessionUserSerializer(many=False)})
|
||||
@action(
|
||||
@ -337,9 +356,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
# since it caches the full object
|
||||
if SESSION_IMPERSONATE_USER in request.session:
|
||||
request.session[SESSION_IMPERSONATE_USER] = new_user
|
||||
serializer = SessionUserSerializer(data={"user": UserSelfSerializer(request.user).data})
|
||||
serializer.is_valid()
|
||||
return Response(serializer.data)
|
||||
return Response({"user": data.data})
|
||||
|
||||
@permission_required("authentik_core.view_user", ["authentik_events.view_event"])
|
||||
@extend_schema(responses={200: UserMetricsSerializer(many=False)})
|
||||
|
@ -10,6 +10,9 @@ SESSION_IMPERSONATE_USER = "authentik_impersonate_user"
|
||||
SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user"
|
||||
LOCAL = local()
|
||||
RESPONSE_HEADER_ID = "X-authentik-id"
|
||||
KEY_AUTH_VIA = "auth_via"
|
||||
KEY_USER = "user"
|
||||
INTERNAL_HEADER_PREFIX = "X-authentik-internal-"
|
||||
|
||||
|
||||
class ImpersonateMiddleware:
|
||||
@ -50,15 +53,17 @@ class RequestIDMiddleware:
|
||||
}
|
||||
response = self.get_response(request)
|
||||
response[RESPONSE_HEADER_ID] = request.request_id
|
||||
del LOCAL.authentik["request_id"]
|
||||
del LOCAL.authentik["host"]
|
||||
if auth_via := LOCAL.authentik.get(KEY_AUTH_VIA, None):
|
||||
response[INTERNAL_HEADER_PREFIX + KEY_AUTH_VIA] = auth_via
|
||||
response[INTERNAL_HEADER_PREFIX + KEY_USER] = request.user.username
|
||||
for key in list(LOCAL.authentik.keys()):
|
||||
del LOCAL.authentik[key]
|
||||
return response
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def structlog_add_request_id(logger: Logger, method_name: str, event_dict):
|
||||
def structlog_add_request_id(logger: Logger, method_name: str, event_dict: dict):
|
||||
"""If threadlocal has authentik defined, add request_id to log"""
|
||||
if hasattr(LOCAL, "authentik"):
|
||||
event_dict["request_id"] = LOCAL.authentik.get("request_id", "")
|
||||
event_dict["host"] = LOCAL.authentik.get("host", "")
|
||||
event_dict.update(LOCAL.authentik)
|
||||
return event_dict
|
||||
|
@ -39,6 +39,8 @@ 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
|
||||
USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username"
|
||||
USER_ATTRIBUTE_CHANGE_EMAIL = "goauthentik.io/user/can-change-email"
|
||||
USER_ATTRIBUTE_CAN_OVERRIDE_IP = "goauthentik.io/user/override-ips"
|
||||
|
||||
GRAVATAR_URL = "https://secure.gravatar.com"
|
||||
|
@ -6,6 +6,7 @@ from os import environ
|
||||
from boto3.exceptions import Boto3Error
|
||||
from botocore.exceptions import BotoCoreError, ClientError
|
||||
from dbbackup.db.exceptions import CommandConnectorError
|
||||
from django.conf import settings
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
||||
from django.core import management
|
||||
@ -55,24 +56,25 @@ def clean_expired_models(self: MonitoredTask):
|
||||
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))
|
||||
|
||||
|
||||
def should_backup() -> bool:
|
||||
"""Check if we should be doing backups"""
|
||||
if SERVICE_HOST_ENV_NAME in environ and not CONFIG.y("postgresql.s3_backup.bucket"):
|
||||
LOGGER.info("Running in k8s and s3 backups are not configured, skipping")
|
||||
return False
|
||||
if not CONFIG.y_bool("postgresql.backup.enabled"):
|
||||
return False
|
||||
if settings.DEBUG:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@CELERY_APP.task(bind=True, base=MonitoredTask)
|
||||
@prefill_task()
|
||||
def backup_database(self: MonitoredTask): # pragma: no cover
|
||||
"""Database backup"""
|
||||
self.result_timeout_hours = 25
|
||||
if SERVICE_HOST_ENV_NAME in environ and not CONFIG.y("postgresql.s3_backup.bucket"):
|
||||
LOGGER.info("Running in k8s and s3 backups are not configured, skipping")
|
||||
self.set_status(
|
||||
TaskResult(
|
||||
TaskResultStatus.WARNING,
|
||||
[
|
||||
(
|
||||
"Skipping backup as authentik is running in Kubernetes "
|
||||
"without S3 backups configured."
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
if not should_backup():
|
||||
self.set_status(TaskResult(TaskResultStatus.UNKNOWN, ["Backups are not configured."]))
|
||||
return
|
||||
try:
|
||||
start = datetime.now()
|
||||
|
@ -61,7 +61,7 @@
|
||||
{% endfor %}
|
||||
{% if tenant.branding_title != "authentik" %}
|
||||
<li>
|
||||
<a href="https://goauthentik.io">
|
||||
<a href="https://goauthentik.io?utm_source=authentik">
|
||||
{% trans 'Powered by authentik' %}
|
||||
</a>
|
||||
</li>
|
||||
|
@ -2,7 +2,7 @@
|
||||
from django.urls.base import reverse
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.models import USER_ATTRIBUTE_CHANGE_EMAIL, USER_ATTRIBUTE_CHANGE_USERNAME, User
|
||||
from authentik.flows.models import Flow, FlowDesignation
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.tenants.models import Tenant
|
||||
@ -15,6 +15,34 @@ class TestUsersAPI(APITestCase):
|
||||
self.admin = User.objects.get(username="akadmin")
|
||||
self.user = User.objects.create(username="test-user")
|
||||
|
||||
def test_update_self(self):
|
||||
"""Test update_self"""
|
||||
self.client.force_login(self.admin)
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:user-update-self"), data={"username": "foo", "name": "foo"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_update_self_username_denied(self):
|
||||
"""Test update_self"""
|
||||
self.admin.attributes[USER_ATTRIBUTE_CHANGE_USERNAME] = False
|
||||
self.admin.save()
|
||||
self.client.force_login(self.admin)
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:user-update-self"), data={"username": "foo", "name": "foo"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_update_self_email_denied(self):
|
||||
"""Test update_self"""
|
||||
self.admin.attributes[USER_ATTRIBUTE_CHANGE_EMAIL] = False
|
||||
self.admin.save()
|
||||
self.client.force_login(self.admin)
|
||||
response = self.client.put(
|
||||
reverse("authentik_api:user-update-self"), data={"email": "foo", "name": "foo"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_metrics(self):
|
||||
"""Test user's metrics"""
|
||||
self.client.force_login(self.admin)
|
||||
|
@ -141,7 +141,7 @@ class CertificateKeyPairFilter(FilterSet):
|
||||
class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
||||
"""CertificateKeyPair Viewset"""
|
||||
|
||||
queryset = CertificateKeyPair.objects.all()
|
||||
queryset = CertificateKeyPair.objects.exclude(managed__isnull=False)
|
||||
serializer_class = CertificateKeyPairSerializer
|
||||
filterset_class = CertificateKeyPairFilter
|
||||
|
||||
|
@ -98,7 +98,9 @@ def notification_transport(self: MonitoredTask, notification_pk: int, transport_
|
||||
notification: Notification = Notification.objects.filter(pk=notification_pk).first()
|
||||
if not notification:
|
||||
return
|
||||
transport: NotificationTransport = NotificationTransport.objects.get(pk=transport_pk)
|
||||
transport = NotificationTransport.objects.filter(pk=transport_pk).first()
|
||||
if not transport:
|
||||
return
|
||||
transport.send(notification)
|
||||
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL))
|
||||
except NotificationTransportError as exc:
|
||||
|
@ -1,6 +1,4 @@
|
||||
"""Flow Stage API Views"""
|
||||
from typing import Iterable
|
||||
|
||||
from django.urls.base import reverse
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework import mixins
|
||||
@ -15,7 +13,7 @@ from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import MetaNameSerializer, TypeCreateSerializer
|
||||
from authentik.core.types import UserSettingSerializer
|
||||
from authentik.flows.api.flows import FlowSerializer
|
||||
from authentik.flows.models import Stage
|
||||
from authentik.flows.models import ConfigurableStage, Stage
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
LOGGER = get_logger()
|
||||
@ -86,9 +84,11 @@ class StageViewSet(
|
||||
@action(detail=False, pagination_class=None, filter_backends=[])
|
||||
def user_settings(self, request: Request) -> Response:
|
||||
"""Get all stages the user can configure"""
|
||||
_all_stages: Iterable[Stage] = Stage.objects.all().select_subclasses().order_by("name")
|
||||
stages = []
|
||||
for configurable_stage in all_subclasses(ConfigurableStage):
|
||||
stages += list(configurable_stage.objects.all().order_by("name"))
|
||||
matching_stages: list[dict] = []
|
||||
for stage in _all_stages:
|
||||
for stage in stages:
|
||||
user_settings = stage.ui_user_settings
|
||||
if not user_settings:
|
||||
continue
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""authentik flow signals"""
|
||||
from django.core.cache import cache
|
||||
from django.db.models.signals import post_save
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@ -15,6 +15,7 @@ def delete_cache_prefix(prefix: str) -> int:
|
||||
|
||||
|
||||
@receiver(post_save)
|
||||
@receiver(pre_delete)
|
||||
# pylint: disable=unused-argument
|
||||
def invalidate_flow_cache(sender, instance, **_):
|
||||
"""Invalidate flow cache when flow is updated"""
|
||||
|
@ -545,6 +545,7 @@ class TestFlowExecutor(APITestCase):
|
||||
"password_fields": False,
|
||||
"primary_action": "Log in",
|
||||
"sources": [],
|
||||
"show_source_labels": False,
|
||||
"user_fields": [UserFields.E_MAIL],
|
||||
},
|
||||
)
|
||||
|
@ -60,6 +60,7 @@ class TestFlowInspector(APITestCase):
|
||||
"password_fields": False,
|
||||
"primary_action": "Log in",
|
||||
"sources": [],
|
||||
"show_source_labels": False,
|
||||
"user_fields": ["username"],
|
||||
},
|
||||
)
|
||||
|
@ -5,6 +5,16 @@ postgresql:
|
||||
user: authentik
|
||||
port: 5432
|
||||
password: 'env://POSTGRES_PASSWORD'
|
||||
backup:
|
||||
enabled: true
|
||||
s3_backup:
|
||||
access_key: ""
|
||||
secret_key: ""
|
||||
bucket: ""
|
||||
region: eu-central-1
|
||||
host: ""
|
||||
location: ""
|
||||
insecure_skip_verify: false
|
||||
|
||||
web:
|
||||
listen: 0.0.0.0:9000
|
||||
@ -56,6 +66,7 @@ outposts:
|
||||
# %(build_hash)s: Build hash if you're running a beta version
|
||||
container_image_base: env://AUTHENTIK_OUTPOSTS__DOCKER_IMAGE_BASE?goauthentik.io/%(type)s:%(version)s
|
||||
|
||||
cookie_domain: null
|
||||
disable_update_check: false
|
||||
avatars: env://AUTHENTIK_AUTHENTIK__AVATARS?gravatar
|
||||
geoip: "./GeoLite2-City.mmdb"
|
||||
@ -63,6 +74,6 @@ geoip: "./GeoLite2-City.mmdb"
|
||||
# Can't currently be configured via environment variables, only yaml
|
||||
footer_links:
|
||||
- name: Documentation
|
||||
href: https://goauthentik.io/docs/
|
||||
href: https://goauthentik.io/docs/?utm_source=authentik
|
||||
- name: authentik Website
|
||||
href: https://goauthentik.io/
|
||||
href: https://goauthentik.io/?utm_source=authentik
|
||||
|
@ -2,6 +2,7 @@
|
||||
from time import sleep
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.text import slugify
|
||||
from docker import DockerClient
|
||||
from docker.errors import DockerException, NotFound
|
||||
from docker.models.containers import Container
|
||||
@ -28,6 +29,17 @@ class DockerController(BaseController):
|
||||
except ServiceConnectionInvalid as exc:
|
||||
raise ControllerException from exc
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get the name of the object this reconciler manages"""
|
||||
return (
|
||||
self.outpost.config.object_naming_template
|
||||
% {
|
||||
"name": slugify(self.outpost.name),
|
||||
"uuid": self.outpost.uuid.hex,
|
||||
}
|
||||
).lower()
|
||||
|
||||
def _get_labels(self) -> dict[str, str]:
|
||||
return {
|
||||
"io.goauthentik.outpost-uuid": self.outpost.pk.hex,
|
||||
@ -102,15 +114,14 @@ class DockerController(BaseController):
|
||||
return image
|
||||
|
||||
def _get_container(self) -> tuple[Container, bool]:
|
||||
container_name = f"authentik-proxy-{self.outpost.uuid.hex}"
|
||||
try:
|
||||
return self.client.containers.get(container_name), False
|
||||
return self.client.containers.get(self.name), False
|
||||
except NotFound:
|
||||
self.logger.info("(Re-)creating container...")
|
||||
image_name = self.try_pull_image()
|
||||
container_args = {
|
||||
"image": image_name,
|
||||
"name": container_name,
|
||||
"name": self.name,
|
||||
"detach": True,
|
||||
"environment": self._get_env(),
|
||||
"labels": self._get_labels(),
|
||||
@ -131,12 +142,23 @@ class DockerController(BaseController):
|
||||
True,
|
||||
)
|
||||
|
||||
def _migrate_container_name(self):
|
||||
"""Migrate 2021.9 to 2021.10+"""
|
||||
old_name = f"authentik-proxy-{self.outpost.uuid.hex}"
|
||||
try:
|
||||
old_container: Container = self.client.containers.get(old_name)
|
||||
old_container.kill()
|
||||
old_container.remove()
|
||||
except NotFound:
|
||||
return
|
||||
|
||||
# pylint: disable=too-many-return-statements
|
||||
def up(self, depth=1):
|
||||
if self.outpost.managed == MANAGED_OUTPOST:
|
||||
return None
|
||||
if depth >= 10:
|
||||
raise ControllerException("Giving up since we exceeded recursion limit.")
|
||||
self._migrate_container_name()
|
||||
try:
|
||||
container, has_been_created = self._get_container()
|
||||
if has_been_created:
|
||||
|
@ -448,7 +448,7 @@ class RefreshToken(ExpiringModel, BaseGrantModel):
|
||||
elif self.provider.sub_mode == SubModes.USER_USERNAME:
|
||||
sub = user.username
|
||||
elif self.provider.sub_mode == SubModes.USER_UPN:
|
||||
sub = user.attributes["upn"]
|
||||
sub = user.attributes.get("upn", user.uid)
|
||||
else:
|
||||
raise ValueError(
|
||||
(
|
||||
|
@ -386,6 +386,9 @@ class AuthorizationFlowInitView(PolicyAccessView):
|
||||
def pre_permission_check(self):
|
||||
"""Check prompt parameter before checking permission/authentication,
|
||||
see https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6"""
|
||||
# Quick sanity check at the beginning to prevent event spamming
|
||||
if len(self.request.GET) < 1:
|
||||
raise Http404
|
||||
try:
|
||||
self.params = OAuthAuthorizationParams.from_request(self.request)
|
||||
except AuthorizeError as error:
|
||||
|
@ -138,7 +138,7 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
|
||||
SCOPE_AK_PROXY,
|
||||
]
|
||||
)
|
||||
self.property_mappings.set(scopes)
|
||||
self.property_mappings.add(*list(scopes))
|
||||
self.redirect_uris = _get_callback_url(self.external_host)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -59,11 +59,13 @@ class AuthNRequestParser:
|
||||
) -> AuthNRequest:
|
||||
root = ElementTree.fromstring(decoded_xml)
|
||||
|
||||
# http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
|
||||
# `AssertionConsumerServiceURL` can be omitted, and we should fallback to the
|
||||
# default ACS URL
|
||||
if "AssertionConsumerServiceURL" not in root.attrib:
|
||||
msg = "Missing 'AssertionConsumerServiceURL' attribute"
|
||||
LOGGER.warning(msg)
|
||||
raise CannotHandleAssertion(msg)
|
||||
request_acs_url = root.attrib["AssertionConsumerServiceURL"]
|
||||
request_acs_url = self.provider.acs_url.lower()
|
||||
else:
|
||||
request_acs_url = root.attrib["AssertionConsumerServiceURL"]
|
||||
|
||||
if self.provider.acs_url.lower() != request_acs_url.lower():
|
||||
msg = (
|
||||
|
@ -3,7 +3,7 @@ from time import time
|
||||
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.middleware import RESPONSE_HEADER_ID
|
||||
from authentik.core.middleware import INTERNAL_HEADER_PREFIX, RESPONSE_HEADER_ID
|
||||
from authentik.root.asgi.types import ASGIApp, Message, Receive, Scope, Send
|
||||
|
||||
ASGI_IP_HEADERS = (
|
||||
@ -26,6 +26,8 @@ class ASGILogger:
|
||||
content_length = 0
|
||||
status_code = 0
|
||||
request_id = ""
|
||||
# Copy all headers starting with X-authentik-internal
|
||||
copied_headers = {}
|
||||
location = ""
|
||||
start = time()
|
||||
|
||||
@ -45,9 +47,19 @@ class ASGILogger:
|
||||
if message["type"] == "http.response.start":
|
||||
response_headers = dict(message["headers"])
|
||||
nonlocal request_id
|
||||
nonlocal copied_headers
|
||||
nonlocal location
|
||||
request_id = response_headers.get(RESPONSE_HEADER_ID.encode(), b"").decode()
|
||||
location = response_headers.get(b"Location", b"").decode()
|
||||
# Copy all internal headers to log, and remove them from the final response
|
||||
for header in list(response_headers.keys()):
|
||||
if not header.decode().startswith(INTERNAL_HEADER_PREFIX):
|
||||
continue
|
||||
copied_headers[
|
||||
header.decode().replace(INTERNAL_HEADER_PREFIX, "")
|
||||
] = response_headers[header].decode()
|
||||
del response_headers[header]
|
||||
message["headers"] = list(response_headers.items())
|
||||
|
||||
if message["type"] == "http.response.body" and not message.get("more_body", True):
|
||||
nonlocal start
|
||||
@ -55,6 +67,7 @@ class ASGILogger:
|
||||
kwargs = {"request_id": request_id}
|
||||
if location != "":
|
||||
kwargs["location"] = location
|
||||
kwargs.update(copied_headers)
|
||||
self.log(scope, runtime, content_length, status_code, **kwargs)
|
||||
await send(message)
|
||||
|
||||
|
@ -72,6 +72,7 @@ _cookie_suffix = "_debug" if DEBUG else ""
|
||||
CSRF_COOKIE_NAME = "authentik_csrf"
|
||||
LANGUAGE_COOKIE_NAME = f"authentik_language{_cookie_suffix}"
|
||||
SESSION_COOKIE_NAME = f"authentik_session{_cookie_suffix}"
|
||||
SESSION_COOKIE_DOMAIN = CONFIG.y("cookie_domain", None)
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
@ -175,6 +176,7 @@ SPECTACULAR_SETTINGS = {
|
||||
"FlowDesignationEnum": "authentik.flows.models.FlowDesignation",
|
||||
"PolicyEngineMode": "authentik.policies.models.PolicyEngineMode",
|
||||
"ProxyMode": "authentik.providers.proxy.models.ProxyMode",
|
||||
"PromptTypeEnum": "authentik.stages.prompt.models.FieldTypes",
|
||||
},
|
||||
"ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE": False,
|
||||
"POSTPROCESSING_HOOKS": [
|
||||
@ -379,7 +381,7 @@ DBBACKUP_CONNECTOR_MAPPING = {
|
||||
"django_prometheus.db.backends.postgresql": "dbbackup.db.postgresql.PgDumpConnector",
|
||||
}
|
||||
DBBACKUP_TMP_DIR = gettempdir() if DEBUG else "/tmp" # nosec
|
||||
if CONFIG.y("postgresql.s3_backup"):
|
||||
if CONFIG.y("postgresql.s3_backup.bucket", "") != "":
|
||||
DBBACKUP_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
|
||||
DBBACKUP_STORAGE_OPTIONS = {
|
||||
"access_key": CONFIG.y("postgresql.s3_backup.access_key"),
|
||||
|
@ -82,6 +82,8 @@ class BaseLDAPSynchronizer:
|
||||
value = mapping.evaluate(user=None, request=None, ldap=kwargs, dn=object_dn)
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, (bytes)):
|
||||
continue
|
||||
object_field = mapping.object_field
|
||||
if object_field.startswith("attributes."):
|
||||
# Because returning a list might desired, we can't
|
||||
|
@ -40,6 +40,9 @@ class GroupLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
self._logger.debug("Creating group with attributes", **defaults)
|
||||
if "name" not in defaults:
|
||||
raise IntegrityError("Name was not set by propertymappings")
|
||||
# Special check for `users` field, as this is an M2M relation, and cannot be sync'd
|
||||
if "users" in defaults:
|
||||
del defaults["users"]
|
||||
ak_group, created = Group.objects.update_or_create(
|
||||
**{
|
||||
f"attributes__{LDAP_UNIQUENESS}": uniq,
|
||||
|
@ -3,7 +3,7 @@ from django.urls.base import reverse_lazy
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_field
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, SerializerMethodField
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, SerializerMethodField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ValidationError
|
||||
@ -31,6 +31,7 @@ class SourceTypeSerializer(PassiveSerializer):
|
||||
class OAuthSourceSerializer(SourceSerializer):
|
||||
"""OAuth Source Serializer"""
|
||||
|
||||
provider_type = ChoiceField(choices=MANAGER.get_name_tuple())
|
||||
callback_url = SerializerMethodField()
|
||||
|
||||
def get_callback_url(self, instance: OAuthSource) -> str:
|
||||
|
@ -21,6 +21,9 @@ class UserOAuthSourceConnectionSerializer(SourceSerializer):
|
||||
"source",
|
||||
"identifier",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"user": {"read_only": True},
|
||||
}
|
||||
|
||||
|
||||
class UserOAuthSourceConnectionViewSet(
|
||||
@ -38,3 +41,4 @@ class UserOAuthSourceConnectionViewSet(
|
||||
filterset_fields = ["source__slug"]
|
||||
permission_classes = [OwnerPermissions]
|
||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
ordering = ["source__slug"]
|
||||
|
@ -22,6 +22,9 @@ class PlexSourceConnectionSerializer(SourceSerializer):
|
||||
"identifier",
|
||||
"plex_token",
|
||||
]
|
||||
extra_kwargs = {
|
||||
"user": {"read_only": True},
|
||||
}
|
||||
|
||||
|
||||
class PlexSourceConnectionViewSet(
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.8 on 2021-10-31 16:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_authenticator_sms", "0001_squashed_0004_auto_20211014_0936"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="authenticatorsmsstage",
|
||||
name="from_number",
|
||||
field=models.TextField(),
|
||||
),
|
||||
]
|
@ -75,6 +75,7 @@ class AuthenticatorValidateStageTests(APITestCase):
|
||||
},
|
||||
"user_fields": ["username"],
|
||||
"sources": [],
|
||||
"show_source_labels": False,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
<div class="footer">
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td class="aligncenter content-block">Powered by <a href="https://goauthentik.io">authentik</a>.</td>
|
||||
<td class="aligncenter content-block">Powered by <a href="https://goauthentik.io?utm_source=authentik&utm_medium=email">authentik</a>.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -20,6 +20,7 @@ class IdentificationStageSerializer(StageSerializer):
|
||||
"enrollment_flow",
|
||||
"recovery_flow",
|
||||
"sources",
|
||||
"show_source_labels",
|
||||
]
|
||||
|
||||
|
||||
@ -35,5 +36,6 @@ class IdentificationStageViewSet(UsedByMixin, ModelViewSet):
|
||||
"show_matched_user",
|
||||
"enrollment_flow",
|
||||
"recovery_flow",
|
||||
"show_source_labels",
|
||||
]
|
||||
ordering = ["name"]
|
||||
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.8 on 2021-10-31 16:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_identification", "0011_alter_identificationstage_user_fields"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="identificationstage",
|
||||
name="show_source_labels",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -81,6 +81,7 @@ class IdentificationStage(Stage):
|
||||
sources = models.ManyToManyField(
|
||||
Source, default=list, help_text=_("Specify which sources should be shown.")
|
||||
)
|
||||
show_source_labels = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
def serializer(self) -> BaseSerializer:
|
||||
|
@ -57,6 +57,7 @@ class IdentificationChallenge(Challenge):
|
||||
recovery_url = CharField(required=False)
|
||||
primary_action = CharField()
|
||||
sources = LoginSourceSerializer(many=True, required=False)
|
||||
show_source_labels = BooleanField()
|
||||
|
||||
component = CharField(default="ak-stage-identification")
|
||||
|
||||
@ -152,6 +153,7 @@ class IdentificationStageView(ChallengeStageView):
|
||||
"component": "ak-stage-identification",
|
||||
"user_fields": current_stage.user_fields,
|
||||
"password_fields": bool(current_stage.password_stage),
|
||||
"show_source_labels": current_stage.show_source_labels,
|
||||
}
|
||||
)
|
||||
# If the user has been redirected to us whilst trying to access an
|
||||
|
@ -123,6 +123,7 @@ class TestIdentificationStage(APITestCase):
|
||||
"name": "test",
|
||||
}
|
||||
],
|
||||
"show_source_labels": False,
|
||||
"user_fields": ["email"],
|
||||
},
|
||||
)
|
||||
@ -158,6 +159,7 @@ class TestIdentificationStage(APITestCase):
|
||||
{"code": "invalid", "string": "Failed to " "authenticate."}
|
||||
]
|
||||
},
|
||||
"show_source_labels": False,
|
||||
"flow_info": {
|
||||
"background": self.flow.background_url,
|
||||
"cancel_url": reverse("authentik_flows:cancel"),
|
||||
@ -218,6 +220,7 @@ class TestIdentificationStage(APITestCase):
|
||||
"authentik_core:if-flow",
|
||||
kwargs={"flow_slug": "unique-enrollment-string"},
|
||||
),
|
||||
"show_source_labels": False,
|
||||
"primary_action": "Log in",
|
||||
"flow_info": {
|
||||
"background": flow.background_url,
|
||||
@ -267,6 +270,7 @@ class TestIdentificationStage(APITestCase):
|
||||
"authentik_core:if-flow",
|
||||
kwargs={"flow_slug": "unique-recovery-string"},
|
||||
),
|
||||
"show_source_labels": False,
|
||||
"primary_action": "Log in",
|
||||
"flow_info": {
|
||||
"background": flow.background_url,
|
||||
|
@ -4,7 +4,6 @@ from typing import Optional
|
||||
from deepmerge import always_merger
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.http.response import HttpResponseBadRequest
|
||||
from django.shortcuts import get_object_or_404
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.models import in_memory_stage
|
||||
@ -50,7 +49,12 @@ class InvitationStageView(StageView):
|
||||
return self.executor.stage_ok()
|
||||
return self.executor.stage_invalid()
|
||||
|
||||
invite: Invitation = get_object_or_404(Invitation, pk=token)
|
||||
invite: Invitation = Invitation.objects.filter(pk=token).first()
|
||||
if not invite:
|
||||
LOGGER.debug("invalid invitation", token=token)
|
||||
if stage.continue_flow_without_invitation:
|
||||
return self.executor.stage_ok()
|
||||
return self.executor.stage_invalid()
|
||||
self.executor.plan.context[INVITATION_IN_EFFECT] = True
|
||||
self.executor.plan.context[INVITATION] = invite
|
||||
|
||||
@ -79,7 +83,9 @@ class InvitationFinalStageView(StageView):
|
||||
if not invitation:
|
||||
LOGGER.warning("InvitationFinalStageView stage called without invitation")
|
||||
return HttpResponseBadRequest
|
||||
if not invitation.single_use:
|
||||
return self.executor.stage_ok()
|
||||
invitation.delete()
|
||||
token = invitation.invite_uuid.hex
|
||||
if invitation.single_use:
|
||||
invitation.delete()
|
||||
LOGGER.debug("Deleted invitation", token=token)
|
||||
del self.executor.plan.context[INVITATION]
|
||||
return self.executor.stage_ok()
|
||||
|
@ -8,7 +8,7 @@ from django.http import HttpRequest, HttpResponse
|
||||
from django.http.request import QueryDict
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from guardian.shortcuts import get_anonymous_user
|
||||
from rest_framework.fields import BooleanField, CharField, IntegerField
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, IntegerField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
@ -31,7 +31,7 @@ class StagePromptSerializer(PassiveSerializer):
|
||||
|
||||
field_key = CharField()
|
||||
label = CharField(allow_blank=True)
|
||||
type = CharField()
|
||||
type = ChoiceField(choices=FieldTypes.choices)
|
||||
required = BooleanField()
|
||||
placeholder = CharField(allow_blank=True)
|
||||
order = IntegerField()
|
||||
|
@ -38,7 +38,7 @@ func main() {
|
||||
|
||||
if config.G.ErrorReporting.Enabled {
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
||||
Dsn: config.G.ErrorReporting.DSN,
|
||||
AttachStacktrace: true,
|
||||
TracesSampleRate: 0.6,
|
||||
Release: fmt.Sprintf("authentik@%s", constants.VERSION),
|
||||
|
@ -17,7 +17,7 @@ services:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-goauthentik.io/server}:${AUTHENTIK_TAG:-2021.10.1-rc1}
|
||||
image: ${AUTHENTIK_IMAGE:-goauthentik.io/server}:${AUTHENTIK_TAG:-2021.10.2}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -38,7 +38,7 @@ services:
|
||||
- "0.0.0.0:9000:9000"
|
||||
- "0.0.0.0:9443:9443"
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-goauthentik.io/server}:${AUTHENTIK_TAG:-2021.10.1-rc1}
|
||||
image: ${AUTHENTIK_IMAGE:-goauthentik.io/server}:${AUTHENTIK_TAG:-2021.10.2}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
13
go.mod
13
go.mod
@ -9,12 +9,8 @@ require (
|
||||
github.com/garyburd/redigo v1.6.2 // indirect
|
||||
github.com/getsentry/sentry-go v0.11.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.1
|
||||
github.com/go-openapi/analysis v0.20.1 // indirect
|
||||
github.com/go-openapi/errors v0.20.0 // indirect
|
||||
github.com/go-openapi/runtime v0.20.0
|
||||
github.com/go-openapi/strfmt v0.20.3
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/validate v0.20.2 // indirect
|
||||
github.com/go-openapi/runtime v0.21.0
|
||||
github.com/go-openapi/strfmt v0.21.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
@ -26,15 +22,14 @@ require (
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba
|
||||
github.com/pires/go-proxyproto v0.6.1
|
||||
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
|
||||
go.mongodb.org/mongo-driver v1.5.2 // indirect
|
||||
goauthentik.io/api v0.202198.6
|
||||
goauthentik.io/api v0.2021101.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
|
||||
|
44
go.sum
44
go.sum
@ -150,8 +150,8 @@ github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
|
||||
github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM=
|
||||
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8=
|
||||
github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
@ -162,8 +162,9 @@ github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
@ -173,16 +174,17 @@ github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2e
|
||||
github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
|
||||
github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
|
||||
github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
|
||||
github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc=
|
||||
github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
|
||||
github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68=
|
||||
github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
|
||||
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
|
||||
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
|
||||
github.com/go-openapi/runtime v0.20.0 h1:DEV4oYH28MqakaabtbxH0cjvlzFegi/15kfUVCfiZW0=
|
||||
github.com/go-openapi/runtime v0.20.0/go.mod h1:2WnLRxMiOUWNN0UZskSkxW0+WXdfB1KmqRKCFH+ZWYk=
|
||||
github.com/go-openapi/runtime v0.21.0 h1:giZ8eT26R+/rx6RX2MkYjZPY8vPYVKDhP/mOazrQHzM=
|
||||
github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
@ -192,8 +194,9 @@ github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHK
|
||||
github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
|
||||
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
|
||||
github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
|
||||
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
|
||||
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
@ -203,8 +206,9 @@ github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk
|
||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
|
||||
github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
|
||||
github.com/go-openapi/strfmt v0.20.3 h1:YVG4ZgPZ00km/lRHrIf7c6cKL5/4FAUtG2T9RxWAgDY=
|
||||
github.com/go-openapi/strfmt v0.20.3/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
|
||||
github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
|
||||
github.com/go-openapi/strfmt v0.21.0 h1:hX2qEZKmYks+t0hKeb4VTJpUm2UYsdL3+DCid5swxIs=
|
||||
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
@ -223,8 +227,8 @@ github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbN
|
||||
github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
|
||||
github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
|
||||
github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
|
||||
github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts=
|
||||
github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0=
|
||||
github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw=
|
||||
github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@ -375,6 +379,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -431,8 +436,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaDr2CRKN++7HbSXbefUNDrPq60T+g24s=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
|
||||
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3 h1:NNis9uuNpG5h97Dvxxo53Scg02qBg+3Nfabg6zjFGu8=
|
||||
github.com/nmcclain/ldap v0.0.0-20191021200707-3b3b69a7e9e3/go.mod h1:YtrVB1/v9Td9SyjXpjYVmbdKgj9B0nPTBsdGUxy0i8U=
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba h1:DO8NFYdcRv1dnyAINJIBm6Bw2XibtLvQniNFGzf2W8E=
|
||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba/go.mod h1:4S0XndRL8HNOaQBfdViJ2F/GPCgL524xlXRuXFH12/U=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@ -547,15 +552,15 @@ go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S
|
||||
go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
|
||||
go.mongodb.org/mongo-driver v1.5.2 h1:AsxOLoJTgP6YNM0fXWw4OjdluYmWzQYp+lFJL7xu9fU=
|
||||
go.mongodb.org/mongo-driver v1.5.2/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
|
||||
go.mongodb.org/mongo-driver v1.7.3 h1:G4l/eYY9VrQAK/AUgkV0koQKzQnyddnWxrd/Etf0jIs=
|
||||
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
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.202198.6 h1:guPsIyDBxBRm8Emy3ZP1SWKe1JhfAls2NTZdk+OtW8c=
|
||||
goauthentik.io/api v0.202198.6/go.mod h1:02nnD4FRd8lu8A1+ZuzqownBgvAhdCKzqkKX8v7JMTE=
|
||||
goauthentik.io/api v0.2021101.11 h1:dtdzriw+9Tfh2QolmU82BB/J3piMViAvEwDVByQUco0=
|
||||
goauthentik.io/api v0.2021101.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=
|
||||
@ -644,6 +649,7 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I=
|
||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -709,6 +715,7 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -720,8 +727,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -26,6 +26,7 @@ func DefaultConfig() {
|
||||
LogLevel: "info",
|
||||
ErrorReporting: ErrorReportingConfig{
|
||||
Enabled: false,
|
||||
DSN: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -42,4 +42,5 @@ type ErrorReportingConfig struct {
|
||||
Enabled bool `yaml:"enabled" env:"AUTHENTIK_ERROR_REPORTING__ENABLED"`
|
||||
Environment string `yaml:"environment" env:"AUTHENTIK_ERROR_REPORTING__ENVIRONMENT"`
|
||||
SendPII bool `yaml:"send_pii" env:"AUTHENTIK_ERROR_REPORTING__SEND_PII"`
|
||||
DSN string
|
||||
}
|
||||
|
@ -17,4 +17,4 @@ func OutpostUserAgent() string {
|
||||
return fmt.Sprintf("authentik-outpost@%s (build=%s)", VERSION, BUILD())
|
||||
}
|
||||
|
||||
const VERSION = "2021.10.1-rc1"
|
||||
const VERSION = "2021.10.2"
|
||||
|
@ -35,7 +35,8 @@ type APIController struct {
|
||||
|
||||
logger *log.Entry
|
||||
|
||||
reloadOffset time.Duration
|
||||
reloadOffset time.Duration
|
||||
lastWsReconnect time.Time
|
||||
|
||||
wsConn *recws.RecConn
|
||||
instanceUUID uuid.UUID
|
||||
@ -142,6 +143,10 @@ func (a *APIController) StartBackgorundTasks() error {
|
||||
"build": constants.BUILD(),
|
||||
}).SetToCurrentTime()
|
||||
}
|
||||
go func() {
|
||||
a.logger.Debug("Starting WS reconnector...")
|
||||
a.startWSReConnector()
|
||||
}()
|
||||
go func() {
|
||||
a.logger.Debug("Starting WS Handler...")
|
||||
a.startWSHandler()
|
||||
|
@ -56,6 +56,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID strfmt.UUID) {
|
||||
if err != nil {
|
||||
ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik")
|
||||
}
|
||||
ac.lastWsReconnect = time.Now()
|
||||
}
|
||||
|
||||
// Shutdown Gracefully stops all workers, disconnects from websocket
|
||||
@ -69,6 +70,20 @@ func (ac *APIController) Shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
func (ac *APIController) startWSReConnector() {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ac *APIController) startWSHandler() {
|
||||
logger := ac.logger.WithField("loop", "ws-handler")
|
||||
for {
|
||||
@ -80,8 +95,7 @@ func (ac *APIController) startWSHandler() {
|
||||
"outpost_type": ac.Server.Type(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
}).Set(0)
|
||||
logger.WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
logger.WithError(err).Warning("ws read error")
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
@ -126,8 +140,7 @@ func (ac *APIController) startWSHealth() {
|
||||
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, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
ac.logger.WithField("loop", "ws-health").WithError(err).Warning("ws write error")
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
} else {
|
||||
|
@ -55,7 +55,7 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
|
||||
if err == nil {
|
||||
return instance.Bind(username, req)
|
||||
} else {
|
||||
ls.log.WithError(err).Debug("Username not for instance")
|
||||
req.log.WithError(err).Debug("Username not for instance")
|
||||
}
|
||||
}
|
||||
req.log.WithField("request", "bind").Warning("No provider found for request")
|
||||
|
@ -52,7 +52,7 @@
|
||||
<p></p>
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
<li>
|
||||
<a href="https://goauthentik.io">
|
||||
<a href="https://goauthentik.io?utm_source=authentik_outpost&utm_campaign=proxy_error">
|
||||
Powered by authentik
|
||||
</a>
|
||||
</li>
|
||||
|
53
internal/web/sentry_proxy.go
Normal file
53
internal/web/sentry_proxy.go
Normal file
@ -0,0 +1,53 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"goauthentik.io/internal/config"
|
||||
)
|
||||
|
||||
type SentryRequest struct {
|
||||
DSN string `json:"dsn"`
|
||||
}
|
||||
|
||||
func (ws *WebServer) APISentryProxy(rw http.ResponseWriter, r *http.Request) {
|
||||
if !config.G.ErrorReporting.Enabled {
|
||||
ws.log.Debug("error reporting disabled")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fullBody, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
ws.log.Debug("failed to read body")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(fullBody), "\n")
|
||||
if len(lines) < 1 {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
sd := SentryRequest{}
|
||||
ws.log.Debug(lines[0])
|
||||
err = json.Unmarshal([]byte(lines[0]), &sd)
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to parse sentry request")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if sd.DSN != config.G.ErrorReporting.DSN {
|
||||
ws.log.WithField("have", sd.DSN).WithField("expected", config.G.ErrorReporting.DSN).Debug("invalid DSN")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
res, err := http.DefaultClient.Post("https://sentry.beryju.org/api/8/envelope/", "application/octet-stream", strings.NewReader(string(fullBody)))
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to proxy sentry")
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(res.StatusCode)
|
||||
}
|
@ -51,10 +51,15 @@ func NewWebServer(g *gounicorn.GoUnicorn) *WebServer {
|
||||
p: g,
|
||||
}
|
||||
ws.configureStatic()
|
||||
ws.configureRoutes()
|
||||
ws.configureProxy()
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *WebServer) configureRoutes() {
|
||||
ws.m.Path("/api/v3/sentry/").HandlerFunc(ws.APISentryProxy)
|
||||
}
|
||||
|
||||
func (ws *WebServer) Start() {
|
||||
go ws.listenPlain()
|
||||
go ws.listenTLS()
|
||||
|
@ -1,14 +1,14 @@
|
||||
# Stage 1: Build
|
||||
FROM docker.io/golang:1.17.2 AS builder
|
||||
FROM docker.io/golang:1.17.2-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
COPY . .
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -o /go/ldap ./cmd/ldap
|
||||
|
||||
# Stage 2: Run
|
||||
FROM gcr.io/distroless/base-debian10:debug
|
||||
FROM gcr.io/distroless/static-debian11:debug
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Stage 1: Build website
|
||||
FROM docker.io/node as web-builder
|
||||
FROM docker.io/node:16 as web-builder
|
||||
|
||||
COPY ./web /static/
|
||||
|
||||
@ -7,7 +7,7 @@ ENV NODE_ENV=production
|
||||
RUN cd /static && npm i && npm run build
|
||||
|
||||
# Stage 2: Build
|
||||
FROM docker.io/golang:1.17.2 AS builder
|
||||
FROM docker.io/golang:1.17.2-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
@ -17,10 +17,11 @@ 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/
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -o /go/proxy ./cmd/proxy
|
||||
|
||||
# Stage 3: Run
|
||||
FROM gcr.io/distroless/base-debian10:debug
|
||||
FROM gcr.io/distroless/static-debian11:debug
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
@ -1,6 +1,20 @@
|
||||
[tool.pyright]
|
||||
ignore = [
|
||||
"**/migrations/**",
|
||||
"**/node_modules/**"
|
||||
]
|
||||
|
||||
reportMissingTypeStubs = false
|
||||
strictParameterNoneValue = true
|
||||
strictDictionaryInference = true
|
||||
strictListInference = true
|
||||
verboseOutput = false
|
||||
pythonVersion = "3.9"
|
||||
pythonPlatform = "Linux"
|
||||
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
target-version = ['py38']
|
||||
target-version = ['py39']
|
||||
exclude = 'node_modules'
|
||||
|
||||
[tool.isort]
|
||||
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"reportMissingTypeStubs": false,
|
||||
"ignore": [
|
||||
"**/migrations/**",
|
||||
"**/node_modules/**"
|
||||
],
|
||||
"strictParameterNoneValue": true,
|
||||
"strictDictionaryInference": true,
|
||||
"strictListInference": true,
|
||||
"verboseOutput": false
|
||||
}
|
360
schema.yml
360
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2021.10.1-rc1
|
||||
version: 2021.10.2
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@beryju.org
|
||||
@ -655,6 +655,27 @@ paths:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/authenticators/all/:
|
||||
get:
|
||||
operationId: authenticators_all_list
|
||||
description: Get all devices for current user
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Device'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/authenticators/duo/:
|
||||
get:
|
||||
operationId: authenticators_duo_list
|
||||
@ -13325,6 +13346,175 @@ paths:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/sources/user_connections/all/:
|
||||
get:
|
||||
operationId: sources_user_connections_all_list
|
||||
description: User-source connection Viewset
|
||||
parameters:
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedUserSourceConnectionList'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/sources/user_connections/all/{id}/:
|
||||
get:
|
||||
operationId: sources_user_connections_all_retrieve
|
||||
description: User-source connection Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user source connection.
|
||||
required: true
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserSourceConnection'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
put:
|
||||
operationId: sources_user_connections_all_update
|
||||
description: User-source connection Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user source connection.
|
||||
required: true
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserSourceConnection'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
patch:
|
||||
operationId: sources_user_connections_all_partial_update
|
||||
description: User-source connection Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user source connection.
|
||||
required: true
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UserSourceConnection'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
delete:
|
||||
operationId: sources_user_connections_all_destroy
|
||||
description: User-source connection Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user source connection.
|
||||
required: true
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/sources/user_connections/all/{id}/used_by/:
|
||||
get:
|
||||
operationId: sources_user_connections_all_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this user source connection.
|
||||
required: true
|
||||
tags:
|
||||
- sources
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
'403':
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
/sources/user_connections/oauth/:
|
||||
get:
|
||||
operationId: sources_user_connections_oauth_list
|
||||
@ -16529,6 +16719,10 @@ paths:
|
||||
name: show_matched_user
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: show_source_labels
|
||||
schema:
|
||||
type: boolean
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
@ -20109,9 +20303,9 @@ components:
|
||||
$ref: '#/components/schemas/FooterLink'
|
||||
readOnly: true
|
||||
default:
|
||||
- href: https://goauthentik.io/docs/
|
||||
- href: https://goauthentik.io/docs/?utm_source=authentik
|
||||
name: Documentation
|
||||
- href: https://goauthentik.io/
|
||||
- href: https://goauthentik.io/?utm_source=authentik
|
||||
name: authentik Website
|
||||
flow_authentication:
|
||||
type: string
|
||||
@ -20169,6 +20363,29 @@ components:
|
||||
$ref: '#/components/schemas/FlowRequest'
|
||||
required:
|
||||
- name
|
||||
Device:
|
||||
type: object
|
||||
description: Serializer for Duo authenticator devices
|
||||
properties:
|
||||
verbose_name:
|
||||
type: string
|
||||
readOnly: true
|
||||
verbose_name_plural:
|
||||
type: string
|
||||
readOnly: true
|
||||
pk:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- name
|
||||
- pk
|
||||
- type
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
DeviceChallenge:
|
||||
type: object
|
||||
description: Single device challenge
|
||||
@ -21446,9 +21663,12 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/LoginSource'
|
||||
show_source_labels:
|
||||
type: boolean
|
||||
required:
|
||||
- password_fields
|
||||
- primary_action
|
||||
- show_source_labels
|
||||
- type
|
||||
- user_fields
|
||||
IdentificationChallengeResponseRequest:
|
||||
@ -21527,6 +21747,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Specify which sources should be shown.
|
||||
show_source_labels:
|
||||
type: boolean
|
||||
required:
|
||||
- component
|
||||
- name
|
||||
@ -21581,6 +21803,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Specify which sources should be shown.
|
||||
show_source_labels:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
IntentEnum:
|
||||
@ -22668,8 +22892,7 @@ components:
|
||||
description: How the source determines if an existing user should be authenticated
|
||||
or a new user enrolled.
|
||||
provider_type:
|
||||
type: string
|
||||
maxLength: 255
|
||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
||||
request_token_url:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -22743,8 +22966,7 @@ components:
|
||||
description: How the source determines if an existing user should be authenticated
|
||||
or a new user enrolled.
|
||||
provider_type:
|
||||
type: string
|
||||
maxLength: 255
|
||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
||||
request_token_url:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -25574,6 +25796,41 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedUserSourceConnectionList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
type: object
|
||||
properties:
|
||||
next:
|
||||
type: number
|
||||
previous:
|
||||
type: number
|
||||
count:
|
||||
type: number
|
||||
current:
|
||||
type: number
|
||||
total_pages:
|
||||
type: number
|
||||
start_index:
|
||||
type: number
|
||||
end_index:
|
||||
type: number
|
||||
required:
|
||||
- next
|
||||
- previous
|
||||
- count
|
||||
- current
|
||||
- total_pages
|
||||
- start_index
|
||||
- end_index
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UserSourceConnection'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedUserWriteStageList:
|
||||
type: object
|
||||
properties:
|
||||
@ -26459,6 +26716,8 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
description: Specify which sources should be shown.
|
||||
show_source_labels:
|
||||
type: boolean
|
||||
PatchedInvitationRequest:
|
||||
type: object
|
||||
description: Invitation Serializer
|
||||
@ -26806,8 +27065,7 @@ components:
|
||||
description: How the source determines if an existing user should be authenticated
|
||||
or a new user enrolled.
|
||||
provider_type:
|
||||
type: string
|
||||
maxLength: 255
|
||||
$ref: '#/components/schemas/ProviderTypeEnum'
|
||||
request_token_url:
|
||||
type: string
|
||||
nullable: true
|
||||
@ -26946,8 +27204,6 @@ components:
|
||||
type: object
|
||||
description: Plex Source connection Serializer
|
||||
properties:
|
||||
user:
|
||||
type: integer
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -27471,8 +27727,6 @@ components:
|
||||
type: object
|
||||
description: OAuth Source Serializer
|
||||
properties:
|
||||
user:
|
||||
type: integer
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -27660,6 +27914,7 @@ components:
|
||||
title: ID
|
||||
user:
|
||||
type: integer
|
||||
readOnly: true
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -27677,8 +27932,6 @@ components:
|
||||
type: object
|
||||
description: Plex Source connection Serializer
|
||||
properties:
|
||||
user:
|
||||
type: integer
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -27690,7 +27943,6 @@ components:
|
||||
- identifier
|
||||
- plex_token
|
||||
- source
|
||||
- user
|
||||
PlexSourceRequest:
|
||||
type: object
|
||||
description: Plex Source Serializer
|
||||
@ -28209,6 +28461,18 @@ components:
|
||||
required:
|
||||
- authorization_flow
|
||||
- name
|
||||
ProviderTypeEnum:
|
||||
enum:
|
||||
- apple
|
||||
- azuread
|
||||
- discord
|
||||
- facebook
|
||||
- github
|
||||
- google
|
||||
- openidconnect
|
||||
- reddit
|
||||
- twitter
|
||||
type: string
|
||||
ProxyMode:
|
||||
enum:
|
||||
- proxy
|
||||
@ -29270,6 +29534,40 @@ components:
|
||||
- slug
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
SourceRequest:
|
||||
type: object
|
||||
description: Source Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: Source's display Name.
|
||||
slug:
|
||||
type: string
|
||||
description: Internal source name, used in URLs.
|
||||
maxLength: 50
|
||||
pattern: ^[-a-zA-Z0-9_]+$
|
||||
enabled:
|
||||
type: boolean
|
||||
authentication_flow:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Flow to use when authenticating existing users.
|
||||
enrollment_flow:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Flow to use when enrolling new users.
|
||||
policy_engine_mode:
|
||||
$ref: '#/components/schemas/PolicyEngineMode'
|
||||
user_matching_mode:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/UserMatchingModeEnum'
|
||||
description: How the source determines if an existing user should be authenticated
|
||||
or a new user enrolled.
|
||||
required:
|
||||
- name
|
||||
- slug
|
||||
SourceType:
|
||||
type: object
|
||||
description: Serializer for SourceType
|
||||
@ -29348,7 +29646,7 @@ components:
|
||||
label:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
$ref: '#/components/schemas/PromptTypeEnum'
|
||||
required:
|
||||
type: boolean
|
||||
placeholder:
|
||||
@ -30004,6 +30302,7 @@ components:
|
||||
title: ID
|
||||
user:
|
||||
type: integer
|
||||
readOnly: true
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -30019,8 +30318,6 @@ components:
|
||||
type: object
|
||||
description: OAuth Source Serializer
|
||||
properties:
|
||||
user:
|
||||
type: integer
|
||||
source:
|
||||
type: string
|
||||
format: uuid
|
||||
@ -30030,7 +30327,6 @@ components:
|
||||
required:
|
||||
- identifier
|
||||
- source
|
||||
- user
|
||||
UserReputation:
|
||||
type: object
|
||||
description: UserReputation Serializer
|
||||
@ -30222,6 +30518,30 @@ components:
|
||||
- component
|
||||
- object_uid
|
||||
- title
|
||||
UserSourceConnection:
|
||||
type: object
|
||||
description: OAuth Source Serializer
|
||||
properties:
|
||||
pk:
|
||||
type: integer
|
||||
readOnly: true
|
||||
title: ID
|
||||
user:
|
||||
type: integer
|
||||
readOnly: true
|
||||
source:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Source'
|
||||
readOnly: true
|
||||
created:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
required:
|
||||
- created
|
||||
- pk
|
||||
- source
|
||||
- user
|
||||
UserWriteStage:
|
||||
type: object
|
||||
description: UserWriteStage Serializer
|
||||
|
@ -1,6 +1,6 @@
|
||||
## @goauthentik/api
|
||||
|
||||
This package provides a generated API Client for [authentik](https://goauthentik.io).
|
||||
This package provides a generated API Client for [authentik](https://goauthentik.io?utm_source=npm-api-package).
|
||||
|
||||
### Building
|
||||
|
||||
|
@ -16,5 +16,7 @@
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"importOrder": ["^@lingui/(.*)$", "^lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
"importOrderSeparation": true
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"importOrderParserPlugins": ["typescript", "classProperties", "decorators-legacy"]
|
||||
}
|
||||
|
2376
web/package-lock.json
generated
2376
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -44,51 +44,51 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/plugin-proposal-decorators": "^7.15.8",
|
||||
"@babel/plugin-transform-runtime": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.0",
|
||||
"@babel/preset-typescript": "^7.16.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@goauthentik/api": "^2021.10.1-rc1-1634651140",
|
||||
"@goauthentik/api": "^2021.10.1-1635796507",
|
||||
"@lingui/cli": "^3.12.1",
|
||||
"@lingui/core": "^3.12.1",
|
||||
"@lingui/detect-locale": "^3.12.1",
|
||||
"@lingui/macro": "^3.12.1",
|
||||
"@patternfly/patternfly": "^4.144.5",
|
||||
"@patternfly/patternfly": "^4.151.4",
|
||||
"@polymer/iron-form": "^3.0.1",
|
||||
"@polymer/paper-input": "^3.2.1",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@sentry/browser": "^6.13.3",
|
||||
"@sentry/tracing": "^6.13.3",
|
||||
"@squoosh/cli": "^0.7.2",
|
||||
"@trivago/prettier-plugin-sort-imports": "^2.0.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.0.0",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/codemirror": "5.60.5",
|
||||
"@types/grecaptcha": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.1.0",
|
||||
"@typescript-eslint/parser": "^5.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.6.0",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^3.5.1",
|
||||
"chart.js": "^3.6.0",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"codemirror": "^5.63.3",
|
||||
"construct-style-sheets-polyfill": "^2.4.16",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint": "^8.1.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-custom-elements": "0.0.2",
|
||||
"eslint-plugin-lit": "^1.6.0",
|
||||
"flowchart.js": "^1.16.0",
|
||||
"eslint-plugin-custom-elements": "0.0.4",
|
||||
"eslint-plugin-lit": "^1.6.1",
|
||||
"flowchart.js": "^1.17.0",
|
||||
"fuse.js": "^6.4.6",
|
||||
"lit": "^2.0.2",
|
||||
"moment": "^2.29.1",
|
||||
"prettier": "^2.4.1",
|
||||
"rapidoc": "^9.1.3",
|
||||
"rollup": "^2.58.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup": "^2.59.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.2",
|
||||
"rollup-plugin-minify-html-literals": "^1.2.6",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import babel from "@rollup/plugin-babel";
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import commonjs from "rollup-plugin-commonjs";
|
||||
import copy from "rollup-plugin-copy";
|
||||
import cssimport from "rollup-plugin-cssimport";
|
||||
import resolve from "rollup-plugin-node-resolve";
|
||||
|
@ -38,17 +38,6 @@ export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
|
||||
if (hint.originalException instanceof Response || hint.originalException instanceof DOMException) {
|
||||
return null;
|
||||
}
|
||||
if (event.exception) {
|
||||
me().then(user => {
|
||||
Sentry.showReportDialog({
|
||||
eventId: event.event_id,
|
||||
user: {
|
||||
email: user.user.email,
|
||||
name: user.user.name,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return event;
|
||||
},
|
||||
});
|
||||
@ -58,12 +47,13 @@ export function configureSentry(canDoPpi: boolean = false): Promise<Config> {
|
||||
const intf = window.location.pathname.replace(/.+if\/(.+)\//, "$1");
|
||||
Sentry.setTag(TAG_SENTRY_COMPONENT, `web/${intf}`);
|
||||
}
|
||||
console.debug("authentik/config: Sentry enabled.");
|
||||
if (config.errorReportingSendPii && canDoPpi) {
|
||||
me().then(user => {
|
||||
Sentry.setUser({ email: user.user.email });
|
||||
console.debug("authentik/config: Sentry with PII enabled.");
|
||||
});
|
||||
} else {
|
||||
console.debug("authentik/config: Sentry enabled.");
|
||||
}
|
||||
}
|
||||
return config;
|
||||
|
@ -213,6 +213,10 @@ html > form > input {
|
||||
option {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
optgroup:checked,
|
||||
option:checked {
|
||||
color: var(--ak-dark-background);
|
||||
}
|
||||
.pf-c-input-group {
|
||||
--pf-c-input-group--BackgroundColor: transparent;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2021.10.1-rc1";
|
||||
export const VERSION = "2021.10.2";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
@ -11,7 +11,7 @@ import "codemirror/mode/xml/xml.js";
|
||||
import "codemirror/mode/yaml/yaml.js";
|
||||
import YAML from "yaml";
|
||||
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import PFExpandableSection from "../../node_modules/@patternfly/patternfly/components/ExpandableSection/expandable-section.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { LitElement, html, CSSResult, TemplateResult, css } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ak-dropdown")
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import { CoreApi } from "@goauthentik/api";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Chart, Plugin, Tick, ChartConfiguration, ChartData, ChartOptions } from "chart.js";
|
||||
import { Chart, ChartConfiguration, ChartData, ChartOptions, Plugin, Tick } from "chart.js";
|
||||
import { Legend, Tooltip } from "chart.js";
|
||||
import { DoughnutController, LineController, BarController } from "chart.js";
|
||||
import { BarController, DoughnutController, LineController } from "chart.js";
|
||||
import { ArcElement, BarElement } from "chart.js";
|
||||
import { TimeScale, LinearScale } from "chart.js";
|
||||
import { LinearScale, TimeScale } from "chart.js";
|
||||
import "chartjs-adapter-moment";
|
||||
|
||||
import { css, CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
|
||||
import { EVENT_REFRESH } from "../../constants";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { CSSResult, html, LitElement, TemplateResult } from "lit";
|
||||
import { CSSResult, LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
import AKGlobal from "../../authentik.css";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import { Event, EventsApi } from "@goauthentik/api";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import { Event, EventsApi } from "@goauthentik/api";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
import { EVENT_REFRESH } from "../../constants";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user