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