mirror of
https://github.com/goauthentik/authentik
synced 2026-04-25 17:15:26 +02:00
Merge branch 'main' into lib-typing
This commit is contained in:
2
.github/actions/setup/action.yml
vendored
2
.github/actions/setup/action.yml
vendored
@@ -21,7 +21,7 @@ runs:
|
||||
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext libkrb5-dev krb5-kdc krb5-user krb5-admin-server
|
||||
- name: Install uv
|
||||
if: ${{ contains(inputs.dependencies, 'python') }}
|
||||
uses: astral-sh/setup-uv@2ddd2b9cb38ad8efd50337e8ab201519a34c9f24 # v5
|
||||
uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v5
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup python
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -142,7 +142,9 @@ updates:
|
||||
labels:
|
||||
- dependencies
|
||||
- package-ecosystem: docker
|
||||
directory: "/"
|
||||
directories:
|
||||
- /
|
||||
- /website
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
|
||||
1
.github/workflows/api-ts-publish.yml
vendored
1
.github/workflows/api-ts-publish.yml
vendored
@@ -15,7 +15,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
|
||||
4
.github/workflows/ci-api-docs.yml
vendored
4
.github/workflows/ci-api-docs.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
env:
|
||||
NODE_ENV: production
|
||||
run: npm run build -w api
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: website/api/build
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
|
||||
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v5
|
||||
with:
|
||||
name: api-docs
|
||||
path: website/api/build
|
||||
|
||||
2
.github/workflows/ci-aws-cfn.yml
vendored
2
.github/workflows/ci-aws-cfn.yml
vendored
@@ -42,6 +42,6 @@ jobs:
|
||||
- check-changes-applied
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
|
||||
1
.github/workflows/ci-docs-source.yml
vendored
1
.github/workflows/ci-docs-source.yml
vendored
@@ -13,7 +13,6 @@ env:
|
||||
|
||||
jobs:
|
||||
publish-source-docs:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
|
||||
4
.github/workflows/ci-docs.yml
vendored
4
.github/workflows/ci-docs.yml
vendored
@@ -61,7 +61,6 @@ jobs:
|
||||
working-directory: website/
|
||||
run: npm run build -w integrations
|
||||
build-container:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
@@ -118,7 +117,6 @@ jobs:
|
||||
- build-container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
allowed-skips: ${{ github.repository == 'goauthentik/authentik-internal' && 'build-container' || '[]' }}
|
||||
|
||||
1
.github/workflows/ci-main-daily.yml
vendored
1
.github/workflows/ci-main-daily.yml
vendored
@@ -9,7 +9,6 @@ on:
|
||||
|
||||
jobs:
|
||||
test-container:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@@ -225,7 +225,7 @@ jobs:
|
||||
- test-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
build:
|
||||
|
||||
3
.github/workflows/ci-outpost.yml
vendored
3
.github/workflows/ci-outpost.yml
vendored
@@ -63,11 +63,10 @@ jobs:
|
||||
- test-unittest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
build-container:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
- ci-outpost-mark
|
||||
|
||||
2
.github/workflows/ci-web.yml
vendored
2
.github/workflows/ci-web.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
- lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@release/v1
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
test:
|
||||
|
||||
@@ -13,7 +13,6 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
|
||||
15
.github/workflows/gh-ghcr-retention.yml
vendored
15
.github/workflows/gh-ghcr-retention.yml
vendored
@@ -5,10 +5,13 @@ on:
|
||||
# schedule:
|
||||
# - cron: "0 0 * * *" # every day at midnight
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run:
|
||||
type: boolean
|
||||
description: Enable dry-run mode
|
||||
|
||||
jobs:
|
||||
clean-ghcr:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
name: Delete old unused container images
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -18,12 +21,12 @@ jobs:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Delete 'dev' containers older than a week
|
||||
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v2
|
||||
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1
|
||||
with:
|
||||
image-names: dev-server,dev-ldap,dev-proxy
|
||||
image-tags: "!gh-next,!gh-main"
|
||||
cut-off: One week ago UTC
|
||||
account-type: org
|
||||
org-name: goauthentik
|
||||
untagged-only: false
|
||||
account: goauthentik
|
||||
tag-selection: untagged
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
skip-tags: gh-next,gh-main
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
|
||||
1
.github/workflows/packages-npm-publish.yml
vendored
1
.github/workflows/packages-npm-publish.yml
vendored
@@ -19,7 +19,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
1
.github/workflows/release-next-branch.yml
vendored
1
.github/workflows/release-next-branch.yml
vendored
@@ -12,7 +12,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
update-next:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
environment: internal-production
|
||||
steps:
|
||||
|
||||
22
.github/workflows/repo-mirror-cleanup.yml
vendored
22
.github/workflows/repo-mirror-cleanup.yml
vendored
@@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Repo - Cleanup internal mirror
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
to_internal:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- if: ${{ env.MIRROR_KEY != '' }}
|
||||
uses: BeryJu/repository-mirroring-action@5cf300935bc2e068f73ea69bcc411a8a997208eb # 5cf300935bc2e068f73ea69bcc411a8a997208eb
|
||||
with:
|
||||
target_repo_url: git@github.com:goauthentik/authentik-internal.git
|
||||
ssh_private_key: ${{ secrets.GH_MIRROR_KEY }}
|
||||
args: --tags --force --prune
|
||||
env:
|
||||
MIRROR_KEY: ${{ secrets.GH_MIRROR_KEY }}
|
||||
21
.github/workflows/repo-mirror.yml
vendored
21
.github/workflows/repo-mirror.yml
vendored
@@ -1,21 +0,0 @@
|
||||
---
|
||||
name: Repo - Mirror to internal
|
||||
|
||||
on: [push, delete]
|
||||
|
||||
jobs:
|
||||
to_internal:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- if: ${{ env.MIRROR_KEY != '' }}
|
||||
uses: BeryJu/repository-mirroring-action@5cf300935bc2e068f73ea69bcc411a8a997208eb # 5cf300935bc2e068f73ea69bcc411a8a997208eb
|
||||
with:
|
||||
target_repo_url: git@github.com:goauthentik/authentik-internal.git
|
||||
ssh_private_key: ${{ secrets.GH_MIRROR_KEY }}
|
||||
args: --tags --force
|
||||
env:
|
||||
MIRROR_KEY: ${{ secrets.GH_MIRROR_KEY }}
|
||||
1
.github/workflows/repo-stale.yml
vendored
1
.github/workflows/repo-stale.yml
vendored
@@ -12,7 +12,6 @@ permissions:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
|
||||
@@ -17,7 +17,6 @@ env:
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
if: ${{ github.repository != 'goauthentik/authentik-internal' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-slim AS node-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:24-trixie-slim@sha256:45babd1b4ce0349fb12c4e24bf017b90b96d52806db32e001e3013f341bef0fe AS node-builder
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
@@ -26,7 +26,7 @@ RUN npm run build && \
|
||||
npm run build:sfe
|
||||
|
||||
# Stage 2: Build go proxy
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS go-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-trixie@sha256:7534a6264850325fcce93e47b87a0e3fddd96b308440245e6ab1325fa8a44c91 AS go-builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -63,7 +63,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/authentik ./cmd/server
|
||||
|
||||
# Stage 3: MaxMind GeoIP
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.1 AS geoip
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.1.1@sha256:faecdca22579730ab0b7dea5aa9af350bb3c93cb9d39845c173639ead30346d2 AS geoip
|
||||
|
||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
||||
ENV GEOIPUPDATE_VERBOSE="1"
|
||||
@@ -76,9 +76,9 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.9.5 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.9.7@sha256:ba4857bf2a068e9bc0e64eed8563b065908a4cd6bfb66b531a9c424c8e25e142 AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips AS python-base
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.9-slim-trixie-fips@sha256:700fc8c1e290bd14e5eaca50b1d8e8c748c820010559cbfb4c4f8dfbe2c4c9ff AS python-base
|
||||
|
||||
ENV VENV_PATH="/ak-root/.venv" \
|
||||
PATH="/lifecycle:/ak-root/.venv/bin:$PATH" \
|
||||
@@ -139,6 +139,7 @@ ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.authors="Authentik Security Inc." \
|
||||
org.opencontainers.image.source="https://github.com/goauthentik/authentik" \
|
||||
org.opencontainers.image.description="goauthentik.io Main server image, see https://goauthentik.io for more info." \
|
||||
org.opencontainers.image.documentation="https://docs.goauthentik.io" \
|
||||
org.opencontainers.image.licenses="https://github.com/goauthentik/authentik/blob/main/LICENSE" \
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-web.yml)
|
||||
[](https://codecov.io/gh/goauthentik/authentik)
|
||||

|
||||
[](https://www.transifex.com/authentik/authentik/)
|
||||
[](https://explore.transifex.com/authentik/authentik/)
|
||||
|
||||
## What is authentik?
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
|
||||
|
||||
(.x being the latest patch release for each version)
|
||||
|
||||
| Version | Supported |
|
||||
| --------- | --------- |
|
||||
| 2025.6.x | ✅ |
|
||||
| 2025.8.x | ✅ |
|
||||
| Version | Supported |
|
||||
| ---------- | ---------- |
|
||||
| 2025.8.x | ✅ |
|
||||
| 2025.10.x | ✅ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Enterprise signals"""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db.models.signals import post_delete, post_save, pre_save
|
||||
@@ -33,7 +33,7 @@ def monitoring_set_enterprise(sender, **kwargs):
|
||||
)
|
||||
GAUGE_LICENSE_USAGE.labels(user_type="internal").set(percentage_internal)
|
||||
GAUGE_LICENSE_USAGE.labels(user_type="external").set(percentage_external)
|
||||
GAUGE_LICENSE_EXPIRY.set((summary.latest_valid - now()).total_seconds())
|
||||
GAUGE_LICENSE_EXPIRY.set((summary.latest_valid.replace(tzinfo=UTC) - now()).total_seconds())
|
||||
|
||||
|
||||
@receiver(pre_save, sender=License)
|
||||
|
||||
@@ -49,6 +49,9 @@ def outpost_m2m_changed(sender, instance: Outpost | Provider, action: str, **_):
|
||||
if action not in ["post_add", "post_remove", "post_clear"]:
|
||||
return
|
||||
if isinstance(instance, Outpost):
|
||||
# Rebuild permissions when providers change
|
||||
LOGGER.debug("Rebuilding outpost service account permissions", outpost=instance)
|
||||
instance.build_user_permissions(instance.user)
|
||||
outpost_controller.send_with_options(
|
||||
args=(instance.pk,),
|
||||
rel_obj=instance.service_connection,
|
||||
@@ -92,6 +95,15 @@ def outpost_post_save(sender, instance: Outpost, created: bool, **_):
|
||||
|
||||
def outpost_related_post_save(sender, instance: OutpostServiceConnection | OutpostModel, **_):
|
||||
for outpost in instance.outpost_set.all():
|
||||
# Rebuild permissions in case provider's required objects changed
|
||||
if isinstance(instance, OutpostModel):
|
||||
LOGGER.info(
|
||||
"Provider changed, rebuilding permissions and sending update",
|
||||
outpost=outpost.name,
|
||||
provider=instance.name if hasattr(instance, "name") else str(instance),
|
||||
)
|
||||
outpost.build_user_permissions(outpost.user)
|
||||
LOGGER.debug("Sending update to outpost", outpost=outpost.name, trigger="provider_change")
|
||||
outpost_send_update.send_with_options(
|
||||
args=(outpost.pk,),
|
||||
rel_obj=outpost,
|
||||
|
||||
@@ -378,9 +378,11 @@ class TokenParams:
|
||||
except (PyJWTError, ValueError, TypeError, AttributeError) as exc:
|
||||
LOGGER.warning("failed to parse JWT for kid lookup", exc=exc)
|
||||
raise TokenError("invalid_grant") from None
|
||||
expected_kid = decode_unvalidated["header"]["kid"]
|
||||
fallback_alg = decode_unvalidated["header"]["alg"]
|
||||
expected_kid = decode_unvalidated["header"].get("kid")
|
||||
fallback_alg = decode_unvalidated["header"].get("alg")
|
||||
token = source = None
|
||||
if not expected_kid or not fallback_alg:
|
||||
return None, None
|
||||
for source in self.provider.jwt_federation_sources.filter(
|
||||
oidc_jwks__keys__contains=[{"kid": expected_kid}]
|
||||
):
|
||||
|
||||
@@ -429,6 +429,7 @@ DRAMATIQ = {
|
||||
},
|
||||
),
|
||||
("dramatiq.results.middleware.Results", {"store_results": True}),
|
||||
("authentik.tasks.middleware.StartupSignalsMiddleware", {}),
|
||||
("authentik.tasks.middleware.CurrentTask", {}),
|
||||
("authentik.tasks.middleware.TenantMiddleware", {}),
|
||||
("authentik.tasks.middleware.ModelDataMiddleware", {}),
|
||||
|
||||
@@ -143,7 +143,7 @@ class OAuth2Client(BaseOAuthClient):
|
||||
if self.source.source_type.urls_customizable and self.source.pkce:
|
||||
pkce_mode = self.source.pkce
|
||||
if pkce_mode != PKCEMethod.NONE:
|
||||
verifier = generate_id()
|
||||
verifier = generate_id(length=128)
|
||||
self.request.session[SESSION_KEY_OAUTH_PKCE] = verifier
|
||||
# https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
||||
if pkce_mode == PKCEMethod.PLAIN:
|
||||
|
||||
@@ -205,6 +205,7 @@ class TestOAuthSource(APITestCase):
|
||||
session = self.client.session
|
||||
state = session[f"oauth-client-{self.source.name}-request-state"]
|
||||
verifier = session[SESSION_KEY_OAUTH_PKCE]
|
||||
self.assertEqual(len(verifier), 128)
|
||||
challenge = pkce_s256_challenge(verifier)
|
||||
|
||||
self.assertEqual(qs["redirect_uri"], ["http://testserver/source/oauth/callback/test/"])
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,10 +11,10 @@ from authentik.stages.invitation.models import Invitation, InvitationStage
|
||||
from authentik.stages.invitation.signals import invitation_used
|
||||
from authentik.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||
|
||||
INVITATION_TOKEN_KEY_CONTEXT = "token" # nosec
|
||||
INVITATION_TOKEN_KEY = "itoken" # nosec
|
||||
INVITATION_IN_EFFECT = "invitation_in_effect"
|
||||
INVITATION = "invitation"
|
||||
QS_INVITATION_TOKEN_KEY = "itoken" # nosec
|
||||
PLAN_CONTEXT_INVITATION_TOKEN = "token" # nosec
|
||||
PLAN_CONTEXT_INVITATION_IN_EFFECT = "invitation_in_effect"
|
||||
PLAN_CONTEXT_INVITATION = "invitation"
|
||||
|
||||
|
||||
class InvitationStageView(StageView):
|
||||
@@ -23,13 +23,13 @@ class InvitationStageView(StageView):
|
||||
def get_token(self) -> str | None:
|
||||
"""Get token from saved get-arguments or prompt_data"""
|
||||
# Check for ?token= and ?itoken=
|
||||
if INVITATION_TOKEN_KEY in self.request.session.get(SESSION_KEY_GET, {}):
|
||||
return self.request.session[SESSION_KEY_GET][INVITATION_TOKEN_KEY]
|
||||
if INVITATION_TOKEN_KEY_CONTEXT in self.request.session.get(SESSION_KEY_GET, {}):
|
||||
return self.request.session[SESSION_KEY_GET][INVITATION_TOKEN_KEY_CONTEXT]
|
||||
if QS_INVITATION_TOKEN_KEY in self.request.session.get(SESSION_KEY_GET, {}):
|
||||
return self.request.session[SESSION_KEY_GET][QS_INVITATION_TOKEN_KEY]
|
||||
if PLAN_CONTEXT_INVITATION_TOKEN in self.request.session.get(SESSION_KEY_GET, {}):
|
||||
return self.request.session[SESSION_KEY_GET][PLAN_CONTEXT_INVITATION_TOKEN]
|
||||
# Check for {'token': ''} in the context
|
||||
if INVITATION_TOKEN_KEY_CONTEXT in self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}):
|
||||
return self.executor.plan.context[PLAN_CONTEXT_PROMPT][INVITATION_TOKEN_KEY_CONTEXT]
|
||||
if PLAN_CONTEXT_INVITATION_TOKEN in self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}):
|
||||
return self.executor.plan.context[PLAN_CONTEXT_PROMPT][PLAN_CONTEXT_INVITATION_TOKEN]
|
||||
return None
|
||||
|
||||
def get_invite(self) -> Invitation | None:
|
||||
@@ -60,8 +60,8 @@ class InvitationStageView(StageView):
|
||||
return self.executor.stage_ok()
|
||||
return self.executor.stage_invalid(_("Invalid invite/invite not found"))
|
||||
|
||||
self.executor.plan.context[INVITATION_IN_EFFECT] = True
|
||||
self.executor.plan.context[INVITATION] = invite
|
||||
self.executor.plan.context[PLAN_CONTEXT_INVITATION_IN_EFFECT] = True
|
||||
self.executor.plan.context[PLAN_CONTEXT_INVITATION] = invite
|
||||
|
||||
context = {}
|
||||
always_merger.merge(context, self.executor.plan.context.get(PLAN_CONTEXT_PROMPT, {}))
|
||||
|
||||
@@ -16,9 +16,9 @@ from authentik.flows.tests.test_executor import TO_STAGE_RESPONSE_MOCK
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.stages.invitation.models import Invitation, InvitationStage
|
||||
from authentik.stages.invitation.stage import (
|
||||
INVITATION_TOKEN_KEY,
|
||||
INVITATION_TOKEN_KEY_CONTEXT,
|
||||
PLAN_CONTEXT_INVITATION_TOKEN,
|
||||
PLAN_CONTEXT_PROMPT,
|
||||
QS_INVITATION_TOKEN_KEY,
|
||||
)
|
||||
from authentik.stages.password import BACKEND_INBUILT
|
||||
from authentik.stages.password.stage import PLAN_CONTEXT_AUTHENTICATION_BACKEND
|
||||
@@ -89,7 +89,7 @@ class TestInvitationStage(FlowTestCase):
|
||||
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
base_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
args = urlencode({INVITATION_TOKEN_KEY: invite.pk.hex})
|
||||
args = urlencode({QS_INVITATION_TOKEN_KEY: invite.pk.hex})
|
||||
response = self.client.get(base_url + f"?query={args}")
|
||||
|
||||
session = self.client.session
|
||||
@@ -114,7 +114,7 @@ class TestInvitationStage(FlowTestCase):
|
||||
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
base_url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
args = urlencode({INVITATION_TOKEN_KEY: invite.pk.hex})
|
||||
args = urlencode({QS_INVITATION_TOKEN_KEY: invite.pk.hex})
|
||||
response = self.client.get(base_url + f"?query={args}")
|
||||
|
||||
session = self.client.session
|
||||
@@ -134,7 +134,7 @@ class TestInvitationStage(FlowTestCase):
|
||||
)
|
||||
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
plan.context[PLAN_CONTEXT_PROMPT] = {INVITATION_TOKEN_KEY_CONTEXT: invite.pk.hex}
|
||||
plan.context[PLAN_CONTEXT_PROMPT] = {PLAN_CONTEXT_INVITATION_TOKEN: invite.pk.hex}
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
|
||||
@@ -23,6 +23,7 @@ from authentik import authentik_full_version
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.sentry import should_ignore_exception
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.root.signals import post_startup, pre_startup, startup
|
||||
from authentik.tasks.models import Task, TaskLog, TaskStatus, WorkerStatus
|
||||
from authentik.tenants.models import Tenant
|
||||
from authentik.tenants.utils import get_current_tenant
|
||||
@@ -32,6 +33,14 @@ HEALTHCHECK_LOGGER = get_logger("authentik.worker").bind()
|
||||
DB_ERRORS = (OperationalError, Error)
|
||||
|
||||
|
||||
class StartupSignalsMiddleware(Middleware):
|
||||
def after_process_boot(self, broker: Broker):
|
||||
_startup_sender = type("WorkerStartup", (object,), {})
|
||||
pre_startup.send(sender=_startup_sender)
|
||||
startup.send(sender=_startup_sender)
|
||||
post_startup.send(sender=_startup_sender)
|
||||
|
||||
|
||||
class CurrentTask(BaseCurrentTask):
|
||||
@classmethod
|
||||
def get_task(cls) -> Task:
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django_dramatiq_postgres.models import TaskBase, TaskState
|
||||
|
||||
from authentik.events.logs import LogEvent
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.lib.utils.errors import exception_to_dict
|
||||
from authentik.tenants.models import Tenant
|
||||
@@ -174,7 +175,7 @@ class TaskLog(models.Model):
|
||||
log_level=log_event.log_level,
|
||||
logger=log_event.logger,
|
||||
timestamp=log_event.timestamp,
|
||||
attributes=log_event.attributes,
|
||||
attributes=sanitize_item(log_event.attributes),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -193,7 +194,7 @@ class TaskLog(models.Model):
|
||||
log_level=log_event.log_level,
|
||||
logger=log_event.logger,
|
||||
timestamp=log_event.timestamp,
|
||||
attributes=log_event.attributes,
|
||||
attributes=sanitize_item(log_event.attributes),
|
||||
)
|
||||
for log_event in log_events
|
||||
]
|
||||
|
||||
@@ -5,10 +5,3 @@ setup()
|
||||
import django # noqa: E402
|
||||
|
||||
django.setup()
|
||||
|
||||
from authentik.root.signals import post_startup, pre_startup, startup # noqa: E402
|
||||
|
||||
_startup_sender = type("WorkerStartup", (object,), {})
|
||||
pre_startup.send(sender=_startup_sender)
|
||||
startup.send(sender=_startup_sender)
|
||||
post_startup.send(sender=_startup_sender)
|
||||
|
||||
@@ -5,7 +5,8 @@ from json import loads
|
||||
from django.urls import reverse
|
||||
from django_tenants.utils import get_public_schema_name
|
||||
|
||||
from authentik.core.models import Token, TokenIntents, User
|
||||
from authentik.core.models import Token, TokenIntents
|
||||
from authentik.core.tests.utils import create_test_user
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.tenants.models import Tenant
|
||||
@@ -21,7 +22,7 @@ class TestRecovery(TenantAPITestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.tenant = Tenant.objects.get(schema_name=get_public_schema_name())
|
||||
self.user: User = User.objects.create_user(username="recovery-test-user")
|
||||
self.user = create_test_user()
|
||||
|
||||
@CONFIG.patch("outposts.disable_embedded_outpost", True)
|
||||
@CONFIG.patch("tenants.enabled", True)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func OpensslVersion() string {
|
||||
cmd := exec.Command("openssl", "version")
|
||||
cmd := exec.Command("/usr/bin/openssl", "version")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
|
||||
@@ -93,7 +93,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
}),
|
||||
)
|
||||
if len(outposts.Results) < 1 {
|
||||
log.Panic("No outposts found with given token, ensure the given token corresponds to an authenitk Outpost")
|
||||
log.Panic("No outposts found with given token, ensure the given token corresponds to an authentik Outpost")
|
||||
}
|
||||
outpost := outposts.Results[0]
|
||||
|
||||
@@ -122,6 +122,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
eventHandlers: []EventHandler{},
|
||||
refreshHandlers: make([]func(), 0),
|
||||
}
|
||||
ac.logger.WithField("embedded", ac.IsEmbedded()).Info("Outpost mode")
|
||||
ac.logger.WithField("offset", ac.reloadOffset.String()).Debug("HA Reload offset")
|
||||
err = ac.initEvent(akURL, outpost.Pk)
|
||||
if err != nil {
|
||||
@@ -135,6 +136,13 @@ func (a *APIController) Log() *log.Entry {
|
||||
return a.logger
|
||||
}
|
||||
|
||||
func (a *APIController) IsEmbedded() bool {
|
||||
if m := a.Outpost.Managed.Get(); m != nil {
|
||||
return *m == "goauthentik.io/outposts/embedded"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Start Starts all handlers, non-blocking
|
||||
func (a *APIController) Start() error {
|
||||
err := a.Server.Refresh()
|
||||
|
||||
@@ -66,6 +66,7 @@ type Server interface {
|
||||
API() *ak.APIController
|
||||
Apps() []*Application
|
||||
CryptoStore() *ak.CryptoStore
|
||||
SessionBackend() string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -94,10 +95,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
|
||||
CallbackSignature: []string{"true"},
|
||||
}.Encode()
|
||||
|
||||
isEmbedded := false
|
||||
if m := server.API().Outpost.Managed.Get(); m != nil {
|
||||
isEmbedded = *m == "goauthentik.io/outposts/embedded"
|
||||
}
|
||||
isEmbedded := server.API().IsEmbedded()
|
||||
// Configure an OpenID Connect aware OAuth2 client.
|
||||
endpoint := GetOIDCEndpoint(
|
||||
p,
|
||||
@@ -153,6 +151,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, server Server, old
|
||||
go a.authHeaderCache.Start()
|
||||
if oldApp != nil && oldApp.sessions != nil {
|
||||
a.sessions = oldApp.sessions
|
||||
muxLogger.Debug("reusing existing session store")
|
||||
} else {
|
||||
sess, err := a.getStore(p, externalHost)
|
||||
if err != nil {
|
||||
|
||||
@@ -64,7 +64,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims {
|
||||
|
||||
// Claims are always stored as types.Claims but may be deserialized differently:
|
||||
// - Filesystem store (gob): preserves struct type as types.Claims
|
||||
// - PostgreSQL store (JSON): deserializes as map[string]interface{}
|
||||
// - PostgreSQL store (JSON): deserializes as map[string]any
|
||||
|
||||
// Handle struct type (filesystem store)
|
||||
if c, ok := claims.(types.Claims); ok {
|
||||
@@ -72,7 +72,7 @@ func (a *Application) getClaimsFromSession(r *http.Request) *types.Claims {
|
||||
}
|
||||
|
||||
// Handle map type (PostgreSQL store)
|
||||
if claimsMap, ok := claims.(map[string]interface{}); ok {
|
||||
if claimsMap, ok := claims.(map[string]any); ok {
|
||||
var c types.Claims
|
||||
if err := mapstructure.Decode(claimsMap, &c); err != nil {
|
||||
return nil
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -27,7 +28,7 @@ func TestClaimsJSONSerialization(t *testing.T) {
|
||||
Entitlements: []string{"read", "write"},
|
||||
Sid: "session-id-456",
|
||||
Proxy: &types.ProxyClaims{
|
||||
UserAttributes: map[string]interface{}{
|
||||
UserAttributes: map[string]any{
|
||||
"custom_field": "custom_value",
|
||||
"department": "engineering",
|
||||
},
|
||||
@@ -70,35 +71,33 @@ func TestClaimsJSONSerialization(t *testing.T) {
|
||||
assert.Equal(t, "engineering", parsedClaims.Proxy.UserAttributes["department"])
|
||||
}
|
||||
|
||||
// TestClaimsMapSerialization tests that Claims stored as map[string]interface{} can be converted back
|
||||
// TestClaimsMapSerialization tests that Claims stored as map[string]any can be converted back
|
||||
func TestClaimsMapSerialization(t *testing.T) {
|
||||
// Simulate how claims are stored in session as map (like from PostgreSQL JSONB)
|
||||
claimsMap := map[string]interface{}{
|
||||
claimsMap := map[string]any{
|
||||
"sub": "user-id-123",
|
||||
"exp": float64(1234567890), // json numbers become float64
|
||||
"email": "test@example.com",
|
||||
"email_verified": true,
|
||||
"name": "Test User",
|
||||
"preferred_username": "testuser",
|
||||
"groups": []interface{}{"admin", "user"},
|
||||
"entitlements": []interface{}{"read", "write"},
|
||||
"groups": []any{"admin", "user"},
|
||||
"entitlements": []any{"read", "write"},
|
||||
"sid": "session-id-456",
|
||||
"ak_proxy": map[string]interface{}{
|
||||
"user_attributes": map[string]interface{}{
|
||||
"ak_proxy": map[string]any{
|
||||
"user_attributes": map[string]any{
|
||||
"custom_field": "custom_value",
|
||||
},
|
||||
"backend_override": "custom-backend",
|
||||
"host_header": "example.com",
|
||||
"is_superuser": true,
|
||||
},
|
||||
"raw_token": "not-a-real-token",
|
||||
}
|
||||
|
||||
// Convert map to Claims using JSON marshaling (like getClaimsFromSession does)
|
||||
jsonData, err := json.Marshal(claimsMap)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Convert map to Claims using mapstructure marshaling (like getClaimsFromSession does)
|
||||
var claims types.Claims
|
||||
err = json.Unmarshal(jsonData, &claims)
|
||||
err := mapstructure.Decode(claimsMap, &claims)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify fields
|
||||
@@ -111,6 +110,7 @@ func TestClaimsMapSerialization(t *testing.T) {
|
||||
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
|
||||
assert.Equal(t, []string{"read", "write"}, claims.Entitlements)
|
||||
assert.Equal(t, "session-id-456", claims.Sid)
|
||||
assert.Equal(t, "not-a-real-token", claims.RawToken)
|
||||
|
||||
// Verify proxy claims
|
||||
require.NotNil(t, claims.Proxy)
|
||||
@@ -122,7 +122,7 @@ func TestClaimsMapSerialization(t *testing.T) {
|
||||
|
||||
// TestClaimsMinimalFields tests that Claims work with minimal required fields
|
||||
func TestClaimsMinimalFields(t *testing.T) {
|
||||
claimsMap := map[string]interface{}{
|
||||
claimsMap := map[string]any{
|
||||
"sub": "user-id-123",
|
||||
"exp": float64(1234567890),
|
||||
}
|
||||
@@ -144,11 +144,11 @@ func TestClaimsMinimalFields(t *testing.T) {
|
||||
|
||||
// TestClaimsWithEmptyArrays tests that empty arrays are handled correctly
|
||||
func TestClaimsWithEmptyArrays(t *testing.T) {
|
||||
claimsMap := map[string]interface{}{
|
||||
claimsMap := map[string]any{
|
||||
"sub": "user-id-123",
|
||||
"exp": float64(1234567890),
|
||||
"groups": []interface{}{},
|
||||
"entitlements": []interface{}{},
|
||||
"groups": []any{},
|
||||
"entitlements": []any{},
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(claimsMap)
|
||||
@@ -167,7 +167,7 @@ func TestClaimsWithEmptyArrays(t *testing.T) {
|
||||
|
||||
// TestClaimsWithNullProxyClaims tests that null proxy claims don't cause issues
|
||||
func TestClaimsWithNullProxyClaims(t *testing.T) {
|
||||
claimsMap := map[string]interface{}{
|
||||
claimsMap := map[string]any{
|
||||
"sub": "user-id-123",
|
||||
"exp": float64(1234567890),
|
||||
"ak_proxy": nil,
|
||||
@@ -185,18 +185,18 @@ func TestClaimsWithNullProxyClaims(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestGetClaimsFromSession_Success tests successful retrieval of claims from session
|
||||
// uses a mock session that returns claims as map[string]interface{} to simulate
|
||||
// uses a mock session that returns claims as map[string]any to simulate
|
||||
// how PostgreSQL storage deserializes JSONB data
|
||||
func TestGetClaimsFromSession_Success(t *testing.T) {
|
||||
// Create a custom mock store that returns claims as map
|
||||
store := &mockMapSessionStore{
|
||||
claimsMap: map[string]interface{}{
|
||||
claimsMap: map[string]any{
|
||||
"sub": "user-id-123",
|
||||
"exp": float64(1234567890),
|
||||
"email": "test@example.com",
|
||||
"email_verified": true,
|
||||
"preferred_username": "testuser",
|
||||
"groups": []interface{}{"admin", "user"},
|
||||
"groups": []any{"admin", "user"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -217,9 +217,9 @@ func TestGetClaimsFromSession_Success(t *testing.T) {
|
||||
assert.Equal(t, []string{"admin", "user"}, claims.Groups)
|
||||
}
|
||||
|
||||
// mockMapSessionStore is a mock session store that returns claims as map[string]interface{}
|
||||
// mockMapSessionStore is a mock session store that returns claims as map[string]any
|
||||
type mockMapSessionStore struct {
|
||||
claimsMap map[string]interface{}
|
||||
claimsMap map[string]any
|
||||
}
|
||||
|
||||
func (m *mockMapSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) {
|
||||
@@ -314,7 +314,7 @@ func TestClaimsRoundTrip(t *testing.T) {
|
||||
Entitlements: []string{"ent1", "ent2"},
|
||||
Sid: "session-789",
|
||||
Proxy: &types.ProxyClaims{
|
||||
UserAttributes: map[string]interface{}{
|
||||
UserAttributes: map[string]any{
|
||||
"attr1": "value1",
|
||||
"attr2": float64(42),
|
||||
"attr3": true,
|
||||
@@ -329,8 +329,8 @@ func TestClaimsRoundTrip(t *testing.T) {
|
||||
jsonData, err := json.Marshal(originalClaims)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Step 2: Deserialize to map[string]interface{} (simulating PostgreSQL load)
|
||||
var claimsMap map[string]interface{}
|
||||
// Step 2: Deserialize to map[string]any (simulating PostgreSQL load)
|
||||
var claimsMap map[string]any
|
||||
err = json.Unmarshal(jsonData, &claimsMap)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -29,9 +29,12 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
||||
// Add one to the validity to ensure we don't have a session with indefinite length
|
||||
maxAge = int(*t) + 1
|
||||
}
|
||||
if a.isEmbedded {
|
||||
|
||||
sessionBackend := a.srv.SessionBackend()
|
||||
switch sessionBackend {
|
||||
case "postgres":
|
||||
// New PostgreSQL store
|
||||
ps, err := postgresstore.NewPostgresStore()
|
||||
ps, err := postgresstore.NewPostgresStore(a.log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -46,30 +49,32 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
a.log.Trace("using postgresql session backend")
|
||||
return ps, nil
|
||||
}
|
||||
dir := os.TempDir()
|
||||
cs, err := filesystemstore.GetPersistentStore(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.Codecs = codecs.CodecsFromPairs(maxAge, []byte(*p.CookieSecret))
|
||||
// https://github.com/markbates/goth/commit/7276be0fdf719ddff753f3574ef0f967e4a5a5f7
|
||||
// set the maxLength of the cookies stored on the disk to a larger number to prevent issues with:
|
||||
// securecookie: the value is too long
|
||||
// when using OpenID Connect, since this can contain a large amount of extra information in the id_token
|
||||
case "filesystem":
|
||||
dir := os.TempDir()
|
||||
cs, err := filesystemstore.GetPersistentStore(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.Codecs = codecs.CodecsFromPairs(maxAge, []byte(*p.CookieSecret))
|
||||
// https://github.com/markbates/goth/commit/7276be0fdf719ddff753f3574ef0f967e4a5a5f7
|
||||
// set the maxLength of the cookies stored on the disk to a larger number to prevent issues with:
|
||||
// securecookie: the value is too long
|
||||
// when using OpenID Connect, since this can contain a large amount of extra information in the id_token
|
||||
|
||||
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
|
||||
cs.MaxLength(math.MaxInt)
|
||||
cs.Options.HttpOnly = true
|
||||
cs.Options.Secure = strings.ToLower(externalHost.Scheme) == "https"
|
||||
cs.Options.Domain = *p.CookieDomain
|
||||
cs.Options.SameSite = http.SameSiteLaxMode
|
||||
cs.Options.MaxAge = maxAge
|
||||
cs.Options.Path = "/"
|
||||
a.log.WithField("dir", dir).Trace("using filesystem session backend")
|
||||
return cs, nil
|
||||
// Note, when using the FilesystemStore only the session.ID is written to a browser cookie, so this is explicit for the storage on disk
|
||||
cs.MaxLength(math.MaxInt)
|
||||
cs.Options.HttpOnly = true
|
||||
cs.Options.Secure = strings.ToLower(externalHost.Scheme) == "https"
|
||||
cs.Options.Domain = *p.CookieDomain
|
||||
cs.Options.SameSite = http.SameSiteLaxMode
|
||||
cs.Options.MaxAge = maxAge
|
||||
cs.Options.Path = "/"
|
||||
return cs, nil
|
||||
default:
|
||||
a.log.WithField("backend", sessionBackend).Panic("unknown session backend type")
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Application) SessionName() string {
|
||||
|
||||
@@ -41,6 +41,10 @@ func (ts *testServer) Apps() []*Application {
|
||||
return ts.apps
|
||||
}
|
||||
|
||||
func (ts *testServer) SessionBackend() string {
|
||||
return "filesystem"
|
||||
}
|
||||
|
||||
func newTestApplication() *Application {
|
||||
ts := newTestServer()
|
||||
a, _ := NewApplication(
|
||||
|
||||
48
internal/outpost/proxyv2/postgresstore/logger.go
Normal file
48
internal/outpost/proxyv2/postgresstore/logger.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package postgresstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
gormlogger "gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
type logrusLogger struct {
|
||||
logger *log.Entry
|
||||
}
|
||||
|
||||
func NewLogger(parent *log.Entry) *logrusLogger {
|
||||
return &logrusLogger{
|
||||
logger: parent,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logrusLogger) LogMode(gormlogger.LogLevel) gormlogger.Interface {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Info(ctx context.Context, s string, args ...interface{}) {
|
||||
l.logger.WithContext(ctx).Infof(s, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Warn(ctx context.Context, s string, args ...interface{}) {
|
||||
l.logger.WithContext(ctx).Warnf(s, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Error(ctx context.Context, s string, args ...interface{}) {
|
||||
l.logger.WithContext(ctx).Errorf(s, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
|
||||
elapsed := time.Since(begin)
|
||||
sql, _ := fc()
|
||||
fields := log.Fields{
|
||||
"elapsed": elapsed,
|
||||
}
|
||||
if err != nil {
|
||||
l.logger.WithContext(ctx).WithFields(fields).WithError(err).Error(sql)
|
||||
return
|
||||
}
|
||||
l.logger.WithContext(ctx).WithFields(fields).Trace(sql)
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -18,7 +17,6 @@ import (
|
||||
_ "gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"goauthentik.io/internal/config"
|
||||
"goauthentik.io/internal/outpost/proxyv2/constants"
|
||||
@@ -159,12 +157,12 @@ func SetupGORMWithRefreshablePool(cfg config.PostgreSQLConfig, gormConfig *gorm.
|
||||
}
|
||||
|
||||
// NewPostgresStore returns a new PostgresStore
|
||||
func NewPostgresStore() (*PostgresStore, error) {
|
||||
func NewPostgresStore(log *log.Entry) (*PostgresStore, error) {
|
||||
cfg := config.Get().PostgreSQL
|
||||
|
||||
// Configure GORM
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
Logger: NewLogger(log),
|
||||
NowFunc: func() time.Time {
|
||||
return time.Now().UTC()
|
||||
},
|
||||
@@ -437,39 +435,6 @@ func generateSessionID() string {
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
||||
var (
|
||||
globalStore *PostgresStore
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
// GetPersistentStore creates a new postgres store if it is the first time the function has been called.
|
||||
// If the function is called multiple times, the store from the variable is returned to ensure that only one instance is running.
|
||||
func GetPersistentStore() (*PostgresStore, error) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if globalStore == nil {
|
||||
store, err := NewPostgresStore()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create persistent store: %w", err)
|
||||
}
|
||||
globalStore = store
|
||||
}
|
||||
|
||||
return globalStore, nil
|
||||
}
|
||||
|
||||
// StopPersistentStore stops the cleanup background job and clears the globalStore variable.
|
||||
func StopPersistentStore() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if globalStore != nil {
|
||||
_ = globalStore.Close()
|
||||
globalStore = nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewTestStore creates a PostgresStore for testing with the given database and pool.
|
||||
// The pool reference is required to properly close connections in test cleanup.
|
||||
func NewTestStore(db *gorm.DB, pool *RefreshableConnPool) *PostgresStore {
|
||||
|
||||
@@ -55,6 +55,11 @@ func NewProxyServer(ac *ak.APIController) ak.Outpost {
|
||||
if ac.GlobalConfig.ErrorReporting.Enabled {
|
||||
globalMux.Use(sentryhttp.New(sentryhttp.Options{}).Handle)
|
||||
}
|
||||
if ac.IsEmbedded() {
|
||||
l.Info("using PostgreSQL session backend")
|
||||
} else {
|
||||
l.Info("using filesystem session backend")
|
||||
}
|
||||
s := &ProxyServer{
|
||||
cryptoStore: ak.NewCryptoStore(ac.Client.CryptoApi),
|
||||
apps: make(map[string]*application.Application),
|
||||
@@ -190,7 +195,7 @@ func (ps *ProxyServer) Stop() error {
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) serve(listener net.Listener) {
|
||||
srv := &http.Server{Handler: ps.mux}
|
||||
srv := web.Server(ps.mux)
|
||||
|
||||
// See https://golang.org/pkg/net/http/#Server.Shutdown
|
||||
idleConnsClosed := make(chan struct{})
|
||||
|
||||
@@ -15,7 +15,9 @@ import (
|
||||
)
|
||||
|
||||
func (ps *ProxyServer) Refresh() error {
|
||||
providers, err := ak.Paginator(ps.akAPI.Client.OutpostsApi.OutpostsProxyList(context.Background()), ak.PaginatorOptions{
|
||||
req := ps.akAPI.Client.OutpostsApi.OutpostsProxyList(context.Background())
|
||||
ps.log.WithField("outpost_pk", ps.akAPI.Outpost.Pk).Debug("Requesting providers for outpost")
|
||||
providers, err := ak.Paginator(req, ak.PaginatorOptions{
|
||||
PageSize: 100,
|
||||
Logger: ps.log,
|
||||
})
|
||||
@@ -25,6 +27,13 @@ func (ps *ProxyServer) Refresh() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ps.log.WithField("count", len(providers)).Debug("Fetched providers")
|
||||
if len(providers) == 0 {
|
||||
ps.log.Warning("No providers assigned to this outpost, check outpost configuration in authentik")
|
||||
}
|
||||
for i, p := range providers {
|
||||
ps.log.WithField("index", i).WithField("name", p.Name).WithField("external_host", p.ExternalHost).WithField("assigned_to_app", p.AssignedApplicationName).Debug("Provider details")
|
||||
}
|
||||
apps := make(map[string]*application.Application)
|
||||
for _, provider := range providers {
|
||||
rsp := sentry.StartSpan(context.Background(), "authentik.outposts.proxy.application_ss")
|
||||
@@ -52,6 +61,7 @@ func (ps *ProxyServer) Refresh() error {
|
||||
ps.log.WithError(err).Warning("failed to setup application")
|
||||
continue
|
||||
}
|
||||
ps.log.WithField("name", provider.Name).WithField("host", externalHost.Host).Info("Loaded application")
|
||||
apps[externalHost.Host] = a
|
||||
}
|
||||
ps.apps = apps
|
||||
@@ -70,3 +80,14 @@ func (ps *ProxyServer) CryptoStore() *ak.CryptoStore {
|
||||
func (ps *ProxyServer) Apps() []*application.Application {
|
||||
return maps.Values(ps.apps)
|
||||
}
|
||||
|
||||
func (ps *ProxyServer) SessionBackend() string {
|
||||
if ps.akAPI.IsEmbedded() {
|
||||
return "postgres"
|
||||
}
|
||||
if !ps.akAPI.IsEmbedded() {
|
||||
return "filesystem"
|
||||
}
|
||||
ps.log.Panic("failed to determine session backend type")
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1,59 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
|
||||
<title>{{.Title}}</title>
|
||||
<link rel="shortcut icon" type="image/png" href="/outpost.goauthentik.io/static/dist/assets/icons/icon.png">
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/patternfly.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/outpost.goauthentik.io/static/dist/authentik.css">
|
||||
<link rel="prefetch" href="/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg" />
|
||||
<style>
|
||||
.pf-c-background-image::before {
|
||||
--ak-flow-background: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:root {
|
||||
--ak-flow-background: url("/outpost.goauthentik.io/static/dist/assets/images/flow_background.jpg");
|
||||
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--sm-2x: var(--ak-flow-background);
|
||||
--pf-c-background-image--BackgroundImage--lg: var(--ak-flow-background);
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 16px;
|
||||
font-size: clamp(16px, 3dvw, 20px);
|
||||
background-color: #000;
|
||||
background-color: Canvas;
|
||||
color: #fff;
|
||||
color: CanvasText;
|
||||
font-family: system-ui, ui-sans-serif, sans-serif;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
min-height: 100dvh;
|
||||
margin: 0;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
border-color: Field;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 2.75rem;
|
||||
max-width: 90dvw;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="pf-c-background-image">
|
||||
</div>
|
||||
<div class="pf-c-login stacked">
|
||||
<div class="ak-login-container">
|
||||
<main class="pf-c-login__main">
|
||||
<div class="pf-c-login__main-header pf-c-brand ak-brand">
|
||||
<img src="/outpost.goauthentik.io/static/dist/assets/icons/icon_left_brand.svg" alt="authentik Logo" />
|
||||
</div>
|
||||
<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
{{ .Title }}
|
||||
</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
{{ .Message }}
|
||||
</div>
|
||||
<div class="pf-c-login__main-body">
|
||||
<a href="/" class="pf-c-button pf-m-primary pf-m-block">Go to home</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="pf-c-login__footer">
|
||||
<ul class="pf-c-list pf-m-inline">
|
||||
<li>
|
||||
<span>
|
||||
Powered by authentik
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
<svg
|
||||
class="logo"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1000 144.29"
|
||||
aria-label="authentik"
|
||||
aria-role="heading"
|
||||
aria-level="1"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M106 41.08h25.39v101.2H106v-10.7a50 50 0 0 1-14.92 10.19 41.84 41.84 0 0 1-16.21 3.11q-19.61 0-33.91-15.21T26.64 91.86q0-23.43 13.85-38.41t33.63-15a42.78 42.78 0 0 1 17.09 3.44A46.82 46.82 0 0 1 106 52.24ZM79.29 61.91a25.65 25.65 0 0 0-19.56 8.33q-7.78 8.33-7.79 21.34t7.93 21.58a25.66 25.66 0 0 0 19.51 8.47 26.15 26.15 0 0 0 19.84-8.33q7.88-8.33 7.88-21.81 0-13.2-7.88-21.39t-19.93-8.19ZM168.39 41.08h25.67v48.74q0 14.22 2 19.76a17.24 17.24 0 0 0 6.29 8.61 18.06 18.06 0 0 0 10.65 3.07 18.6 18.6 0 0 0 10.77-3 17.7 17.7 0 0 0 6.57-8.88q1.59-4.36 1.59-18.7v-49.6h25.39V84q0 26.51-4.18 36.27a39.6 39.6 0 0 1-15.07 18.28q-10 6.38-25.3 6.37-16.65 0-26.93-7.44t-14.48-20.78q-3-9.21-3-33.49ZM297.3 3.78h25.39v37.3h15.07v21.85h-15.07v79.35H297.3V62.93h-13V41.08h13ZM362.86 2h25.21v49.3a57.74 57.74 0 0 1 15-9.63 38.56 38.56 0 0 1 15.25-3.21 34.36 34.36 0 0 1 25.39 10.42q8.83 9 8.84 26.51v66.88h-25V97.91q0-17.58-1.68-23.81t-5.71-9.3a16.07 16.07 0 0 0-10-3.07 18.85 18.85 0 0 0-13.26 5.11q-5.53 5.11-7.67 14-1.12 4.56-1.12 20.84v40.65h-25.25ZM589.91 99h-81.58q1.77 10.78 9.44 17.16t19.58 6.37a33.86 33.86 0 0 0 24.46-10l21.4 10a50.54 50.54 0 0 1-19.16 16.79q-11.16 5.44-26.51 5.44-23.82 0-38.79-15t-15-37.63q0-23.16 14.93-38.46t37.44-15.3q23.91 0 38.88 15.3t15 40.42Zm-25.4-20a25.48 25.48 0 0 0-9.92-13.77A28.81 28.81 0 0 0 537.4 60a30.42 30.42 0 0 0-18.64 5.95q-5 3.72-9.31 13.12ZM621.89 41.08h25.39v10.37q8.64-7.29 15.65-10.13a37.82 37.82 0 0 1 14.35-2.85A34.77 34.77 0 0 1 702.83 49q8.82 8.94 8.82 26.42v66.88h-25.11V98q0-18.12-1.63-24.06a16.44 16.44 0 0 0-5.66-9.06 15.8 15.8 0 0 0-10-3.11 18.73 18.73 0 0 0-13.23 5.15q-5.49 5.08-7.62 14.22-1.12 4.74-1.12 20.54v40.6h-25.39ZM750.71 3.78h25.39v37.3h15.07v21.85H776.1v79.35h-25.39V62.93h-13V41.08h13ZM826.09-.6a15.55 15.55 0 0 1 11.45 4.84A16.08 16.08 0 0 1 842.31 16a15.87 15.87 0 0 1-4.72 11.58 15.34 15.34 0 0 1-11.32 4.79 15.6 15.6 0 0 1-11.55-4.88 16.35 16.35 0 0 1-4.72-11.9 15.57 15.57 0 0 1 4.73-11.44A15.53 15.53 0 0 1 826.09-.6ZM813.39 41.08h25.39v101.2h-25.39zM873.47 2h25.39v80.8l37.39-41.72h31.89l-43.59 48.5 48.81 52.7h-31.53l-43-46.64v46.64h-25.36Z"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<hr />
|
||||
|
||||
<div role="alert" aria-live="assertive">
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p>{{ .Message }}</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package types
|
||||
|
||||
type ProxyClaims struct {
|
||||
UserAttributes map[string]interface{} `json:"user_attributes"`
|
||||
BackendOverride string `json:"backend_override"`
|
||||
HostHeader string `json:"host_header"`
|
||||
IsSuperuser bool `json:"is_superuser"`
|
||||
UserAttributes map[string]any `json:"user_attributes" mapstructure:"user_attributes"`
|
||||
BackendOverride string `json:"backend_override" mapstructure:"backend_override"`
|
||||
HostHeader string `json:"host_header" mapstructure:"host_header"`
|
||||
IsSuperuser bool `json:"is_superuser" mapstructure:"is_superuser"`
|
||||
}
|
||||
|
||||
type Claims struct {
|
||||
@@ -19,5 +19,5 @@ type Claims struct {
|
||||
Sid string `json:"sid" mapstructure:"sid"`
|
||||
Proxy *ProxyClaims `json:"ak_proxy" mapstructure:"ak_proxy"`
|
||||
|
||||
RawToken string `mapstructure:"-"`
|
||||
RawToken string `json:"raw_token" mapstructure:"raw_token"`
|
||||
}
|
||||
|
||||
@@ -41,95 +41,92 @@ func (pi *ProviderInstance) SetEAPState(key string, state *protocol.State) {
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetEAPSettings() protocol.Settings {
|
||||
protocols := []protocol.ProtocolConstructor{
|
||||
identity.Protocol,
|
||||
legacy_nak.Protocol,
|
||||
settings := protocol.Settings{
|
||||
Logger: &logrusAdapter{pi.log},
|
||||
Protocols: []protocol.ProtocolConstructor{
|
||||
identity.Protocol,
|
||||
legacy_nak.Protocol,
|
||||
},
|
||||
}
|
||||
|
||||
certId := pi.certId
|
||||
if certId == "" {
|
||||
return protocol.Settings{
|
||||
Protocols: protocols,
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
cert := pi.s.cryptoStore.Get(certId)
|
||||
if cert == nil {
|
||||
return protocol.Settings{
|
||||
Protocols: protocols,
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
return protocol.Settings{
|
||||
Logger: &logrusAdapter{entry: pi.log},
|
||||
Protocols: append(protocols, tls.Protocol, peap.Protocol),
|
||||
ProtocolPriority: []protocol.Type{
|
||||
identity.TypeIdentity,
|
||||
tls.TypeTLS,
|
||||
},
|
||||
ProtocolSettings: map[protocol.Type]interface{}{
|
||||
tls.TypeTLS: tls.Settings{
|
||||
Config: &ttls.Config{
|
||||
Certificates: []ttls.Certificate{*cert},
|
||||
ClientAuth: ttls.RequireAnyClientCert,
|
||||
},
|
||||
HandshakeSuccessful: func(ctx protocol.Context, certs []*x509.Certificate) protocol.Status {
|
||||
ident := ctx.GetProtocolState(identity.TypeIdentity).(*identity.State).Identity
|
||||
settings.Protocols = append(settings.Protocols, tls.Protocol, peap.Protocol)
|
||||
settings.ProtocolPriority = []protocol.Type{
|
||||
identity.TypeIdentity,
|
||||
tls.TypeTLS,
|
||||
}
|
||||
settings.ProtocolSettings = map[protocol.Type]any{
|
||||
tls.TypeTLS: tls.Settings{
|
||||
Config: &ttls.Config{
|
||||
Certificates: []ttls.Certificate{*cert},
|
||||
ClientAuth: ttls.RequireAnyClientCert,
|
||||
},
|
||||
HandshakeSuccessful: func(ctx protocol.Context, certs []*x509.Certificate) protocol.Status {
|
||||
ident := ctx.GetProtocolState(identity.TypeIdentity).(*identity.State).Identity
|
||||
|
||||
ctx.Log().Debug("Starting authn flow")
|
||||
pem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certs[0].Raw,
|
||||
ctx.Log().Debug("Starting authn flow")
|
||||
pem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certs[0].Raw,
|
||||
})
|
||||
|
||||
fe := flow.NewFlowExecutor(context.Background(), pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
"client": utils.GetIP(ctx.Packet().RemoteAddr),
|
||||
"identity": ident,
|
||||
})
|
||||
fe.Answers[flow.StageIdentification] = ident
|
||||
fe.DelegateClientIP(utils.GetIP(ctx.Packet().RemoteAddr))
|
||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
||||
fe.AddHeader("X-Authentik-Outpost-Certificate", url.QueryEscape(string(pem)))
|
||||
|
||||
passed, err := fe.Execute()
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to execute flow", "error", err)
|
||||
return protocol.StatusError
|
||||
}
|
||||
ctx.Log().Debug("Finished flow")
|
||||
if !passed {
|
||||
return protocol.StatusError
|
||||
}
|
||||
access, _, err := fe.ApiClient().OutpostsApi.OutpostsRadiusAccessCheck(context.Background(), pi.providerId).AppSlug(pi.appSlug).Execute()
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to check access: %v", err)
|
||||
return protocol.StatusError
|
||||
}
|
||||
if !access.Access.Passing {
|
||||
ctx.Log().Info("Access denied for user")
|
||||
return protocol.StatusError
|
||||
}
|
||||
if access.HasAttributes() {
|
||||
ctx.AddResponseModifier(func(r, q *radius.Packet) error {
|
||||
rawData, err := base64.StdEncoding.DecodeString(access.GetAttributes())
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to decode attributes from core: %v", err)
|
||||
return errors.New("attribute_decode_failed")
|
||||
}
|
||||
p, err := radius.Parse(rawData, pi.SharedSecret)
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to parse attributes from core: %v", err)
|
||||
return errors.New("attribute_parse_failed")
|
||||
}
|
||||
for _, attr := range p.Attributes {
|
||||
r.Add(attr.Type, attr.Attribute)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
fe := flow.NewFlowExecutor(context.Background(), pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
"client": utils.GetIP(ctx.Packet().RemoteAddr),
|
||||
"identity": ident,
|
||||
})
|
||||
fe.Answers[flow.StageIdentification] = ident
|
||||
fe.DelegateClientIP(utils.GetIP(ctx.Packet().RemoteAddr))
|
||||
fe.Params.Add("goauthentik.io/outpost/radius", "true")
|
||||
fe.AddHeader("X-Authentik-Outpost-Certificate", url.QueryEscape(string(pem)))
|
||||
|
||||
passed, err := fe.Execute()
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to execute flow", "error", err)
|
||||
return protocol.StatusError
|
||||
}
|
||||
ctx.Log().Debug("Finished flow")
|
||||
if !passed {
|
||||
return protocol.StatusError
|
||||
}
|
||||
access, _, err := fe.ApiClient().OutpostsApi.OutpostsRadiusAccessCheck(context.Background(), pi.providerId).AppSlug(pi.appSlug).Execute()
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to check access: %v", err)
|
||||
return protocol.StatusError
|
||||
}
|
||||
if !access.Access.Passing {
|
||||
ctx.Log().Info("Access denied for user")
|
||||
return protocol.StatusError
|
||||
}
|
||||
if access.HasAttributes() {
|
||||
ctx.AddResponseModifier(func(r, q *radius.Packet) error {
|
||||
rawData, err := base64.StdEncoding.DecodeString(access.GetAttributes())
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to decode attributes from core: %v", err)
|
||||
return errors.New("attribute_decode_failed")
|
||||
}
|
||||
p, err := radius.Parse(rawData, pi.SharedSecret)
|
||||
if err != nil {
|
||||
ctx.Log().Warn("failed to parse attributes from core: %v", err)
|
||||
return errors.New("attribute_parse_failed")
|
||||
}
|
||||
for _, attr := range p.Attributes {
|
||||
r.Add(attr.Type, attr.Attribute)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return protocol.StatusSuccess
|
||||
},
|
||||
}
|
||||
return protocol.StatusSuccess
|
||||
},
|
||||
},
|
||||
}
|
||||
return settings
|
||||
}
|
||||
|
||||
17
internal/utils/web/server.go
Normal file
17
internal/utils/web/server.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Server(h http.Handler) *http.Server {
|
||||
return &http.Server{
|
||||
Handler: h,
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
ReadTimeout: 30 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
MaxHeaderBytes: http.DefaultMaxHeaderBytes,
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,7 @@ import (
|
||||
staticWeb "goauthentik.io/web"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthentikStarting = errors.New("authentik starting")
|
||||
)
|
||||
var ErrAuthentikStarting = errors.New("authentik starting")
|
||||
|
||||
const (
|
||||
maxBodyBytes = 32 * 1024 * 1024
|
||||
@@ -99,11 +97,11 @@ func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request
|
||||
|
||||
if strings.Contains(accept, "application/json") {
|
||||
header.Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
err = json.NewEncoder(rw).Encode(map[string]string{
|
||||
"error": "authentik starting",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write error message")
|
||||
return
|
||||
@@ -113,21 +111,18 @@ func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
loadingSplashFile, err := staticWeb.StaticDir.Open("standalone/loading/startup.html")
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to open startup splash screen")
|
||||
return
|
||||
}
|
||||
|
||||
loadingSplashHTML, err := io.ReadAll(loadingSplashFile)
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to read startup splash screen")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = rw.Write(loadingSplashHTML)
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write startup splash screen")
|
||||
return
|
||||
@@ -138,7 +133,6 @@ func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request
|
||||
|
||||
// Fallback to just a status message
|
||||
_, err = rw.Write([]byte("authentik starting"))
|
||||
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to write initializing HTML")
|
||||
}
|
||||
|
||||
@@ -241,9 +241,7 @@ func (ws *WebServer) listenPlain() {
|
||||
}
|
||||
|
||||
func (ws *WebServer) serve(listener net.Listener) {
|
||||
srv := &http.Server{
|
||||
Handler: ws.mainRouter,
|
||||
}
|
||||
srv := web.Server(ws.mainRouter)
|
||||
|
||||
// See https://golang.org/pkg/net/http/#Server.Shutdown
|
||||
idleConnsClosed := make(chan struct{})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-trixie@sha256:7534a6264850325fcce93e47b87a0e3fddd96b308440245e6ab1325fa8a44c91 AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -31,13 +31,14 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/ldap ./cmd/ldap
|
||||
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.authors="Authentik Security Inc." \
|
||||
org.opencontainers.image.source="https://github.com/goauthentik/authentik" \
|
||||
org.opencontainers.image.description="goauthentik.io LDAP outpost, see https://goauthentik.io for more info." \
|
||||
org.opencontainers.image.documentation="https://docs.goauthentik.io" \
|
||||
org.opencontainers.image.licenses="https://github.com/goauthentik/authentik/blob/main/LICENSE" \
|
||||
|
||||
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1031.0",
|
||||
"aws-cdk": "^2.1031.1",
|
||||
"cross-env": "^10.1.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -24,9 +24,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1031.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1031.0.tgz",
|
||||
"integrity": "sha512-iotbdOIvHoLCz1u7PUVDQbcpGpVqMk8HzAeOP4PGRqD9PoAEsCb3mwGTxHMrNGEGWdJXQxiTOGSaMmlwEvACxA==",
|
||||
"version": "2.1031.1",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1031.1.tgz",
|
||||
"integrity": "sha512-y/ovZDtOKvgJFtwE7NDFKRgJB3stgUUrAu6b31icuFO8+vnZ+4BIt0McQfbVR8ky5QOj2TW1G1sk55k8Yv+BnQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1031.0",
|
||||
"aws-cdk": "^2.1031.1",
|
||||
"cross-env": "^10.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,21 +52,23 @@ class BaseMigration:
|
||||
"""Run the actual migration"""
|
||||
|
||||
|
||||
def wait_for_lock(cursor: Cursor):
|
||||
def wait_for_lock(conn: Connection, cursor: Cursor):
|
||||
"""lock an advisory lock to prevent multiple instances from migrating at once"""
|
||||
global LOCKED # noqa: PLW0603
|
||||
LOGGER.info("waiting to acquire database lock")
|
||||
cursor.execute("SELECT pg_advisory_lock(%s)", (ADV_LOCK_UID,))
|
||||
with conn.transaction():
|
||||
cursor.execute("SELECT pg_advisory_lock(%s)", (ADV_LOCK_UID,))
|
||||
LOCKED = True
|
||||
|
||||
|
||||
def release_lock(cursor: Cursor):
|
||||
def release_lock(conn: Connection, cursor: Cursor):
|
||||
"""Release database lock"""
|
||||
global LOCKED # noqa: PLW0603
|
||||
if not LOCKED:
|
||||
return
|
||||
LOGGER.info("releasing database lock")
|
||||
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
|
||||
with conn.transaction():
|
||||
cursor.execute("SELECT pg_advisory_unlock(%s)", (ADV_LOCK_UID,))
|
||||
LOCKED = False
|
||||
|
||||
|
||||
@@ -84,7 +86,7 @@ def run_migrations():
|
||||
)
|
||||
curr = conn.cursor()
|
||||
try:
|
||||
wait_for_lock(curr)
|
||||
wait_for_lock(conn, curr)
|
||||
for migration_path in sorted(
|
||||
Path(__file__).parent.absolute().glob("system_migrations/*.py")
|
||||
):
|
||||
@@ -98,6 +100,7 @@ def run_migrations():
|
||||
if name != "Migration":
|
||||
continue
|
||||
migration = sub(curr, conn)
|
||||
curr.execute(f"SET search_path = {CONFIG.get("postgresql.default_schema")}")
|
||||
if migration.needs_migration():
|
||||
LOGGER.info("Migration needs to be applied", migration=migration_path.name)
|
||||
migration.run()
|
||||
@@ -123,7 +126,7 @@ def run_migrations():
|
||||
check_args.append("--deploy")
|
||||
execute_from_command_line(check_args)
|
||||
finally:
|
||||
release_lock(curr)
|
||||
release_lock(conn, curr)
|
||||
curr.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
BIN
locale/cs_CZ/LC_MESSAGES/django.mo
Normal file
BIN
locale/cs_CZ/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
4221
locale/cs_CZ/LC_MESSAGES/django.po
Normal file
4221
locale/cs_CZ/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-20 00:11+0000\n"
|
||||
"POT-Creation-Date: 2025-10-28 00:10+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -1103,6 +1103,14 @@ msgstr ""
|
||||
msgid "Invalid next URL"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid "Controls the number of objects synced in a single task"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid "Timeout for synchronization of a single page"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid ""
|
||||
"When enabled, provider will not modify or create objects in the remote "
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,15 +1,21 @@
|
||||
import base64
|
||||
import pickle # nosec
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache.backends.base import DEFAULT_TIMEOUT
|
||||
from django.core.cache.backends.db import DatabaseCache as BaseDatabaseCache
|
||||
from django.db import DatabaseError
|
||||
from django.db.utils import ProgrammingError
|
||||
from django.utils.module_loading import import_string
|
||||
from django.utils.timezone import now
|
||||
from psqlextra.types import ConflictAction
|
||||
|
||||
from django_postgres_cache.models import CacheEntry
|
||||
|
||||
|
||||
class DatabaseCache(BaseDatabaseCache):
|
||||
|
||||
def __init__(self, table: str, params: dict[str, Any]) -> None:
|
||||
super().__init__(table, params)
|
||||
self.reverse_key_func = import_string(params["REVERSE_KEY_FUNCTION"])
|
||||
@@ -49,3 +55,87 @@ class DatabaseCache(BaseDatabaseCache):
|
||||
if not entry:
|
||||
return None
|
||||
return int((entry.expires - now()).total_seconds())
|
||||
|
||||
def _base_set_expiry(self, timeout: float | None) -> datetime:
|
||||
timeout = self.get_backend_timeout(timeout)
|
||||
if timeout is None:
|
||||
exp = datetime.max
|
||||
else:
|
||||
tz = UTC if settings.USE_TZ else None
|
||||
exp = datetime.fromtimestamp(timeout, tz=tz)
|
||||
exp.replace(microsecond=0)
|
||||
return exp
|
||||
|
||||
def _base_set_data(
|
||||
self,
|
||||
key: Any,
|
||||
value: Any,
|
||||
timeout: float | None,
|
||||
version: int | None = None,
|
||||
) -> tuple[str, str, datetime]:
|
||||
key = self.make_and_validate_key(key, version=version)
|
||||
pickled = pickle.dumps(value, self.pickle_protocol)
|
||||
# The DB column is expecting a string, so make sure the value is a
|
||||
# string, not bytes. Refs #19274.
|
||||
b64encoded = base64.b64encode(pickled).decode("latin1")
|
||||
|
||||
return (key, b64encoded, self._base_set_expiry(timeout))
|
||||
|
||||
def touch(
|
||||
self,
|
||||
key: Any,
|
||||
timeout: float | None = DEFAULT_TIMEOUT,
|
||||
version: int | None = None,
|
||||
) -> bool:
|
||||
key = self.make_and_validate_key(key, version=version)
|
||||
expiry = self._base_set_expiry(timeout)
|
||||
try:
|
||||
count = CacheEntry.objects.filter(cache_key=key).update(expires=expiry)
|
||||
return bool(count != 0)
|
||||
except DatabaseError:
|
||||
return False
|
||||
|
||||
def add(
|
||||
self,
|
||||
key: Any,
|
||||
value: Any,
|
||||
timeout: float | None = DEFAULT_TIMEOUT,
|
||||
version: int | None = None,
|
||||
) -> bool:
|
||||
key, value, expiry = self._base_set_data(key, value, timeout, version)
|
||||
try:
|
||||
CacheEntry.objects.on_conflict(
|
||||
["cache_key"],
|
||||
ConflictAction.UPDATE,
|
||||
update_values=dict(
|
||||
expires=expiry,
|
||||
),
|
||||
).insert(
|
||||
cache_key=key,
|
||||
value=value,
|
||||
expires=expiry,
|
||||
)
|
||||
# We don't know if the row already existed, we just return True for success
|
||||
return True
|
||||
except DatabaseError:
|
||||
return False
|
||||
|
||||
def set(
|
||||
self,
|
||||
key: Any,
|
||||
value: Any,
|
||||
timeout: float | None = DEFAULT_TIMEOUT,
|
||||
version: int | None = None,
|
||||
) -> None:
|
||||
key, value, expiry = self._base_set_data(key, value, timeout, version)
|
||||
CacheEntry.objects.on_conflict(
|
||||
["cache_key"],
|
||||
ConflictAction.UPDATE,
|
||||
).insert(
|
||||
cache_key=key,
|
||||
value=value,
|
||||
expires=expiry,
|
||||
)
|
||||
|
||||
def clear(self) -> None:
|
||||
CacheEntry.objects.truncate()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 5.2.7 on 2025-10-28 14:04
|
||||
|
||||
import psqlextra.manager.manager
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("django_postgres_cache", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name="cacheentry",
|
||||
managers=[
|
||||
("objects", psqlextra.manager.manager.PostgresManager()), # type: ignore[no-untyped-call]
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -1,12 +1,14 @@
|
||||
from django.db import models
|
||||
from psqlextra.manager import PostgresManager
|
||||
|
||||
|
||||
class CacheEntry(models.Model):
|
||||
|
||||
cache_key = models.TextField(primary_key=True)
|
||||
value = models.TextField()
|
||||
expires = models.DateTimeField(db_index=True)
|
||||
|
||||
objects = PostgresManager() # type: ignore[no-untyped-call]
|
||||
|
||||
class Meta:
|
||||
default_permissions = []
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ classifiers = [
|
||||
|
||||
dependencies = [
|
||||
"django >=4.2,<6.0",
|
||||
"django-postgres-extra >=2.0,<2.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
||||
6
packages/docusaurus-config/package-lock.json
generated
6
packages/docusaurus-config/package-lock.json
generated
@@ -18433,9 +18433,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"version": "13.15.20",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
||||
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
16
packages/eslint-config/package-lock.json
generated
16
packages/eslint-config/package-lock.json
generated
@@ -2118,16 +2118,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react-hooks": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.0.tgz",
|
||||
"integrity": "sha512-fNXaOwvKwq2+pXiRpXc825Vd63+KM4DLL40Rtlycb8m7fYpp6efrTp1sa6ZbP/Ap58K2bEKFXRmhURE+CJAQWw==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
|
||||
"integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.24.4",
|
||||
"@babel/parser": "^7.24.4",
|
||||
"hermes-parser": "^0.25.1",
|
||||
"zod": "^3.22.4 || ^4.0.0",
|
||||
"zod-validation-error": "^3.0.3 || ^4.0.0"
|
||||
"zod": "^3.25.0 || ^4.0.0",
|
||||
"zod-validation-error": "^3.5.0 || ^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -5178,9 +5178,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"version": "13.15.20",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
||||
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
6
packages/prettier-config/package-lock.json
generated
6
packages/prettier-config/package-lock.json
generated
@@ -1760,9 +1760,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "13.15.15",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
|
||||
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
|
||||
"version": "13.15.20",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz",
|
||||
"integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
|
||||
@@ -17,7 +17,7 @@ COPY web .
|
||||
RUN npm run build-proxy
|
||||
|
||||
# Stage 2: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-trixie@sha256:7534a6264850325fcce93e47b87a0e3fddd96b308440245e6ab1325fa8a44c91 AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -47,13 +47,14 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/proxy ./cmd/proxy
|
||||
|
||||
# Stage 3: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.authors="Authentik Security Inc." \
|
||||
org.opencontainers.image.source="https://github.com/goauthentik/authentik" \
|
||||
org.opencontainers.image.description="goauthentik.io Proxy outpost image, see https://goauthentik.io for more info." \
|
||||
org.opencontainers.image.documentation="https://docs.goauthentik.io" \
|
||||
org.opencontainers.image.licenses="https://github.com/goauthentik/authentik/blob/main/LICENSE" \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-trixie@sha256:7534a6264850325fcce93e47b87a0e3fddd96b308440245e6ab1325fa8a44c91 AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -31,13 +31,14 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/rac ./cmd/rac
|
||||
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/guacd:v1.6.0-fips
|
||||
FROM ghcr.io/goauthentik/guacd:v1.6.0-fips@sha256:1d99572b0260924149b8c923c021a32016f885fcea6d5cc8d58f718dfdc7a2dd
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.authors="Authentik Security Inc." \
|
||||
org.opencontainers.image.source="https://github.com/goauthentik/authentik" \
|
||||
org.opencontainers.image.description="goauthentik.io RAC outpost, see https://goauthentik.io for more info." \
|
||||
org.opencontainers.image.documentation="https://docs.goauthentik.io" \
|
||||
org.opencontainers.image.licenses="https://github.com/goauthentik/authentik/blob/main/LICENSE" \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/golang:1.25.3-trixie@sha256:7534a6264850325fcce93e47b87a0e3fddd96b308440245e6ab1325fa8a44c91 AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@@ -31,13 +31,14 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/radius ./cmd/radius
|
||||
|
||||
# Stage 2: Run
|
||||
FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
|
||||
FROM ghcr.io/goauthentik/fips-debian:trixie-slim-fips@sha256:9b4cedf932e97194f1825124830f2eec14254d90162dad28f97e505971543115
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.authors="Authentik Security Inc." \
|
||||
org.opencontainers.image.source="https://github.com/goauthentik/authentik" \
|
||||
org.opencontainers.image.description="goauthentik.io Radius outpost, see https://goauthentik.io for more info." \
|
||||
org.opencontainers.image.documentation="https://docs.goauthentik.io" \
|
||||
org.opencontainers.image.licenses="https://github.com/goauthentik/authentik/blob/main/LICENSE" \
|
||||
|
||||
6
uv.lock
generated
6
uv.lock
generated
@@ -1145,10 +1145,14 @@ version = "0.1.0"
|
||||
source = { editable = "packages/django-postgres-cache" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "django-postgres-extra" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "django", specifier = ">=4.2,<6.0" }]
|
||||
requires-dist = [
|
||||
{ name = "django", specifier = ">=4.2,<6.0" },
|
||||
{ name = "django-postgres-extra", specifier = ">=2.0,<2.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-postgres-extra"
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json",
|
||||
"sourceLocale": "en",
|
||||
"targetLocales": [
|
||||
"en",
|
||||
"cs_CZ",
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"fr",
|
||||
"it",
|
||||
@@ -12,9 +13,9 @@
|
||||
"pl",
|
||||
"ru",
|
||||
"tr",
|
||||
"zh-CN",
|
||||
"zh-Hans",
|
||||
"zh-Hant",
|
||||
"zh-CN",
|
||||
"zh_TW",
|
||||
"pseudo-LOCALE"
|
||||
],
|
||||
|
||||
337
web/package-lock.json
generated
337
web/package-lock.json
generated
@@ -43,13 +43,13 @@
|
||||
"@patternfly/elements": "^4.2.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@sentry/browser": "^10.21.0",
|
||||
"@spotlightjs/spotlight": "^4.2.0",
|
||||
"@storybook/addon-docs": "^9.1.13",
|
||||
"@storybook/addon-links": "^9.1.13",
|
||||
"@storybook/web-components": "^9.1.13",
|
||||
"@storybook/web-components-vite": "^9.1.13",
|
||||
"@types/codemirror": "^5.60.16",
|
||||
"@sentry/browser": "^10.22.0",
|
||||
"@spotlightjs/spotlight": "^4.3.0",
|
||||
"@storybook/addon-docs": "^9.1.15",
|
||||
"@storybook/addon-links": "^9.1.15",
|
||||
"@storybook/web-components": "^9.1.15",
|
||||
"@storybook/web-components-vite": "^9.1.15",
|
||||
"@types/codemirror": "^5.60.17",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.4",
|
||||
"@types/mocha": "^10.0.10",
|
||||
@@ -64,7 +64,6 @@
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.5.1",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chromedriver": "^141.0.4",
|
||||
"codemirror": "^6.0.2",
|
||||
"core-js": "^3.46.0",
|
||||
"country-flag-icons": "^1.5.21",
|
||||
@@ -111,7 +110,7 @@
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.1.11",
|
||||
"vite": "^7.1.12",
|
||||
"vitest": "^3.2.4",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
@@ -1937,9 +1936,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.1.tgz",
|
||||
"integrity": "sha512-j/P+yuxXfgxb+mW7OEoRCM3G47zCTDqUPivJo/VzpjbG8I9csTXtOprCf5FfOfHK4whOJny0aHuBEON+kS7CCA==",
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.20.2.tgz",
|
||||
"integrity": "sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
@@ -3231,9 +3230,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
"version": "28.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.8.tgz",
|
||||
"integrity": "sha512-o1Ug9PxYsF61R7/NXO/GgMZZproLd/WH2XA53Tp9ppf6bU1lMlTtC/gUM6zM3mesi2E0rypk+PNtVrELREyWEQ==",
|
||||
"version": "29.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.0.tgz",
|
||||
"integrity": "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.1",
|
||||
@@ -3628,84 +3627,84 @@
|
||||
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.21.0.tgz",
|
||||
"integrity": "sha512-QRHpCBheLd/88Z2m3ABMriV0MweW+pcGKuVsH61/UdziKcQLdoQpOSvGg0/0CuqFm2UjL7237ZzLdZrWaCOlfQ==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.22.0.tgz",
|
||||
"integrity": "sha512-BpJoLZEyJr7ORzkCrIjxRTnFWwO1mJNICVh3B9g5d9245niGT4OJvRozmLz89WgJkZFHWu84ls6Xfq5b/3tGFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.21.0.tgz",
|
||||
"integrity": "sha512-6SnRR2FiW6TMwCE0PqbueHkkpeVnjOjz00R+/mX25Dp1U5BU5TzbXHzn9Y4wKnaD3Rzz4+nnzVkpHAOL3SppGw==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.22.0.tgz",
|
||||
"integrity": "sha512-zXySOin/gGHPV+yKaHqjN9YZ7psEJwzLn8PzCLeo+4REzF1eQwbYZIgOxJFD32z8s3nZiABSWFM/n1CvVfMEsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.21.0.tgz",
|
||||
"integrity": "sha512-5tfiKZJzZf9+Xk8SyvoC4ZEVLNmjBZZEaKhVyNo53CLWUWfWOqDc3DB9fj85i/yHFQ0ImdRnaPBc0CIeN00CcA==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.22.0.tgz",
|
||||
"integrity": "sha512-JNE4kHAQSG4/V+J+Zog3vKBWgOe9H33ol/MEU1RuLM/4I+uLf4mTetwnS9ilpnnW/Z/gQYfA+R3CiMrZtqTivw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.21.0",
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry-internal/browser-utils": "10.22.0",
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay-canvas": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.21.0.tgz",
|
||||
"integrity": "sha512-TOLo5mAjJSOuJId8Po44d1hwJ5bIZDtRSoupWpYWqLw1tuUh1tc4vqID11ZXsw9pBzjVIK653BPDX/z/9+Um+Q==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.22.0.tgz",
|
||||
"integrity": "sha512-DE4JNUskJg+O+wFq42W5gAa/99aD5k7TfGOwABxvnzFv8vkKA7pqXwPbFFPzypdKIkln+df7RmbnDwQRNg6/lA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/replay": "10.21.0",
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry-internal/replay": "10.22.0",
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.21.0.tgz",
|
||||
"integrity": "sha512-z/63bUFBQkTfJ5ElhWTYvomz+gZ1GsoH16v4/RGoPY5qZgYxcVO3fkp0opnu3gcbXS0ZW7TLRiHpqhvipDdP6g==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.22.0.tgz",
|
||||
"integrity": "sha512-wD2XqN+yeBpQFfdPo6+wlKDMyyuDctVGzZWE4qTPntICKQuwMdAfeq5Ma89ad0Dw+bzG9UijGeyuJQlswF87Mw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.21.0",
|
||||
"@sentry-internal/feedback": "10.21.0",
|
||||
"@sentry-internal/replay": "10.21.0",
|
||||
"@sentry-internal/replay-canvas": "10.21.0",
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry-internal/browser-utils": "10.22.0",
|
||||
"@sentry-internal/feedback": "10.22.0",
|
||||
"@sentry-internal/replay": "10.22.0",
|
||||
"@sentry-internal/replay-canvas": "10.22.0",
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.21.0.tgz",
|
||||
"integrity": "sha512-/+gpOOb2Wr1UbW59WKqNAVVIqFz9FjtUJuPtVh4UanxGCfavMPaKpFzSlaEKJSKDkiCQgANP4O2y8Y5Bh3tvEA==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.22.0.tgz",
|
||||
"integrity": "sha512-V1oeHbrOKzxadsCmgtPku3v3Emo/Bpb3VSuKmlLrQefiHX98MWtjJ3XDGfduzD5/dCdh0r/OOLwjcmrO/PZ2aw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.21.0.tgz",
|
||||
"integrity": "sha512-z7g+rZIHOSzISGCYbpy8b6UxYd7kl0bjdTTjDC4rJCoofhO71By5tZum1HhcmYEWWDj7qc/Mbfmfv6rXoimT6A==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.22.0.tgz",
|
||||
"integrity": "sha512-PfG8AMT2kgFJ7rWb0lLJOmjLW2riytTliLMjfoJ8/tLGk964uKqE0xM7FLtXZjlLJqTXVYCVG7VIPj185uyckQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
@@ -3738,9 +3737,9 @@
|
||||
"@opentelemetry/sdk-trace-base": "^2.1.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.37.0",
|
||||
"@prisma/instrumentation": "6.15.0",
|
||||
"@sentry/core": "10.21.0",
|
||||
"@sentry/node-core": "10.21.0",
|
||||
"@sentry/opentelemetry": "10.21.0",
|
||||
"@sentry/core": "10.22.0",
|
||||
"@sentry/node-core": "10.22.0",
|
||||
"@sentry/opentelemetry": "10.22.0",
|
||||
"import-in-the-middle": "^1.14.2",
|
||||
"minimatch": "^9.0.0"
|
||||
},
|
||||
@@ -3749,14 +3748,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/node-core": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.21.0.tgz",
|
||||
"integrity": "sha512-vPn9sYMl2IB14lp6HP3nyJVM2VDDpclf7yvNWe/9yDY+ad1T/+8x5j501LjUaZDRR+7APM1Mb1S9YMAL3gTiwA==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.22.0.tgz",
|
||||
"integrity": "sha512-88Yyn+Qvmp0kPMnNRWgpUlAvhI9CNPqOT+0glW0L7OoN8LkJcNgx2GGUoLrJ+RGeHz/S7dIJY6DGa+u0Not2Qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apm-js-collab/tracing-hooks": "^0.3.1",
|
||||
"@sentry/core": "10.21.0",
|
||||
"@sentry/opentelemetry": "10.21.0",
|
||||
"@sentry/core": "10.22.0",
|
||||
"@sentry/opentelemetry": "10.22.0",
|
||||
"import-in-the-middle": "^1.14.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3773,12 +3772,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/opentelemetry": {
|
||||
"version": "10.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.21.0.tgz",
|
||||
"integrity": "sha512-Yr4imXxkSLhJt2WHVXh31NpIe9ZgcnJTVVvzq/g6Ox40bj5+cdpFh6RTsLcsw5hvDC8a1KUvmdIhUTKAkEsqgA==",
|
||||
"version": "10.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.22.0.tgz",
|
||||
"integrity": "sha512-XHXYYq3zsQ/dj1kQ7cGGLFIEVRmrmjcMhiJHvmKKsUGKxQjHe2G0LuG8clHIPkmbg7yEIxCT/W2I9QzrwYt5+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.21.0"
|
||||
"@sentry/core": "10.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -3804,15 +3803,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@spotlightjs/overlay": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-4.3.0.tgz",
|
||||
"integrity": "sha512-k1JtQmUKV1mtwbzTtz6Ii95F1o0iV7wNLfZpWHy/UOlWGfYhtk6yUhWo/fFcycCHF9HH+Hse/LVu6OcHuM/v7A==",
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-4.3.1.tgz",
|
||||
"integrity": "sha512-Ngv/w+8e6PW1jmGgKWLerI06F7nDr8HeAYbmM3qVM+03ZoKH0SZYrCUiF5S50XnH2JTW/f88oi/GWsO6qgPBgQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@spotlightjs/sidecar": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-2.2.0.tgz",
|
||||
"integrity": "sha512-GFNW0gMLG6p5W89OMg6BoBuvWbDIjV3yrx6Y3I87U7QSZh5+bFwA6tHSkDQ3jwpUopK700ztkoFrJ+XeDH3FhA==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/sidecar/-/sidecar-2.3.0.tgz",
|
||||
"integrity": "sha512-O4qAM0tAS41zxzhJtth+SAQJAbgJQNHeGokVGPjnyPyXiW61GiapBlZtGzX3GJZaJMJGaMM2ry3ALShonbYPIw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@hono/mcp": "^0.1.1",
|
||||
@@ -3820,30 +3819,45 @@
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"@modelcontextprotocol/sdk": "^1.16.0",
|
||||
"@sentry/core": "^10",
|
||||
"@sentry/node": "^10",
|
||||
"@sentry/node": "^10.22.0",
|
||||
"chalk": "^5.6.2",
|
||||
"eventsource": "^4.0.0",
|
||||
"hono": "^4.10.2",
|
||||
"fast-fuzzy": "^1.12.0",
|
||||
"hono": "^4.10.3",
|
||||
"launch-editor": "^2.9.1",
|
||||
"logfmt": "^1.4.0",
|
||||
"mcp-proxy": "^5.6.0",
|
||||
"uuidv7": "^1.0.2",
|
||||
"zod": "^3"
|
||||
},
|
||||
"bin": {
|
||||
"spotlight-sidecar": "dist/server.js"
|
||||
"spotlight-sidecar": "dist/run.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@spotlightjs/sidecar/node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@spotlightjs/spotlight": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-4.2.0.tgz",
|
||||
"integrity": "sha512-4f2zDbdnBrJx2XDSEBWzKokpexGgjHWflggKGEPWUMvVcGn8Lux/FJMY2Tl2er/IixhU+sPnoMCKW9dJutOmEA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@spotlightjs/spotlight/-/spotlight-4.3.0.tgz",
|
||||
"integrity": "sha512-MYHkTlhs1ClqKG4yv3U68gb1/5qv/zvpdQYlAYMrx5a5i7YUhb2iSyEHyz1G5hzNKcmqsALxFSvoqvADALaU2Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@sentry/node": "^10",
|
||||
"@spotlightjs/overlay": "4.3.0",
|
||||
"@spotlightjs/sidecar": "2.2.0",
|
||||
"@sentry/node": "^10.22.0",
|
||||
"@spotlightjs/overlay": "4.3.1",
|
||||
"@spotlightjs/sidecar": "2.3.0",
|
||||
"import-meta-resolve": "^4.1.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -3854,15 +3868,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-docs": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.13.tgz",
|
||||
"integrity": "sha512-V1nCo7bfC3kQ5VNVq0VDcHsIhQf507m+BxMA5SIYiwdJHljH2BXpW2fL3FFn9gv9Wp57AEEzhm+wh4zANaJgkg==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.15.tgz",
|
||||
"integrity": "sha512-thbBQKzN0ycSEQjtIeXJT4i6+4kWoNIWYo4x2GQ3jtqHsT8T4FZW1n/xK1ysZbB4eCwG2hOn44BRTu8p4S7UoQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@storybook/csf-plugin": "9.1.13",
|
||||
"@storybook/csf-plugin": "9.1.15",
|
||||
"@storybook/icons": "^1.4.0",
|
||||
"@storybook/react-dom-shim": "9.1.13",
|
||||
"@storybook/react-dom-shim": "9.1.15",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
@@ -3872,13 +3886,13 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-links": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-9.1.13.tgz",
|
||||
"integrity": "sha512-wx33RA5PPRSepVAjR0hMFp2IXoPgjwNAHIP92aoi2QQFS3+NHlf1I4vXEPpHU6lc0WBwM43qvLSI0qTAyZd8Nw==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-9.1.15.tgz",
|
||||
"integrity": "sha512-pPrE2rbLzZewsvEN5pbJ4xfc05RADP7MMkaqySNWwcNQwLx0GLvk3ZL+bA/wG73aaPH+w3Gc7gy2En8q5m+DBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0"
|
||||
@@ -3889,7 +3903,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
@@ -3898,12 +3912,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/builder-vite": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.13.tgz",
|
||||
"integrity": "sha512-pmtIjU02ASJOZKdL8DoxWXJgZnpTDgD5WmMnjKJh9FaWmc2YiCW2Y6VRxPox96OM655jYHQe5+UIbk3Cwtwb4A==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.15.tgz",
|
||||
"integrity": "sha512-GZkx72cBnCuTL/cVOIWIicB4GCmZWx52zFGSC/qHOT/sKcUkrIoQSpVljqyPa66woHyUeSZX4mu7aGj5A27QVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/csf-plugin": "9.1.13",
|
||||
"@storybook/csf-plugin": "9.1.15",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
@@ -3911,14 +3925,14 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.1.13",
|
||||
"storybook": "^9.1.15",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/csf-plugin": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.13.tgz",
|
||||
"integrity": "sha512-EMpzYuyt9FDcxxfBChWzfId50y8QMpdenviEQ8m+pa6c+ANx3pC5J6t7y0khD8TQu815sTy+nc6cc8PC45dPUA==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.15.tgz",
|
||||
"integrity": "sha512-UThWh7V3+zd+71XdIsNFkrNslkjyaD/HQPnjWGWBCU4ZWNWRSPd3r0r02nH0zzo+NBi0V4vzNDg/PmfD51iaOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unplugin": "^1.3.1"
|
||||
@@ -3928,7 +3942,7 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/global": {
|
||||
@@ -3950,9 +3964,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react-dom-shim": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.13.tgz",
|
||||
"integrity": "sha512-/tMr9TmV3+98GEQO0S03k4gtKHGCpv9+k9Dmnv+TJK3TBz7QsaFEzMwe3gCgoTaebLACyVveDiZkWnCYAWB6NA==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.15.tgz",
|
||||
"integrity": "sha512-l6smvNwxh6kp2U/BupzQ4/NSraTWysZcAe2x+GO5CiIIB8Jbi41XLu5XIHI/GQRnNqpXXE3uMImiHGOORmHEXA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -3961,13 +3975,13 @@
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/web-components": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-9.1.13.tgz",
|
||||
"integrity": "sha512-fVPpD0WfBiAI65a3azyC/8CeFSF4iiRGNHmwjVOHBVEJ7fb53dFvSxbCq/i8j8xJesvBI9RbqSNWQxp4rK26tw==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-9.1.15.tgz",
|
||||
"integrity": "sha512-OshzxnYcbxU8kOdAuP2ueo9QzeN9ywdPF1n1izhzSHFozavQDjYeJ995DouFVseOIrZnkBSZ62rLW6tv11GIAA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
@@ -3983,17 +3997,17 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lit": "^2.0.0 || ^3.0.0",
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/web-components-vite": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-9.1.13.tgz",
|
||||
"integrity": "sha512-mjdNzLzPEo1HHyDJbfdErxMGY5ctYL5hCy/qrRlxNfHTE/4nSMTfjuFhsMmmVmGBNVDiCLjTaJ/bhSYPkesugg==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-9.1.15.tgz",
|
||||
"integrity": "sha512-Pny+8VLwJcsx4tBysJdRjKRc3niyIum8rSssMkaXes4pCVVWjmk8MMW69Q8odkfiLm4TCMTEO2VsPDsKm3NouA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/builder-vite": "9.1.13",
|
||||
"@storybook/web-components": "9.1.13"
|
||||
"@storybook/builder-vite": "9.1.15",
|
||||
"@storybook/web-components": "9.1.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
@@ -4003,7 +4017,7 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.1.13"
|
||||
"storybook": "^9.1.15"
|
||||
}
|
||||
},
|
||||
"node_modules/@swagger-api/apidom-ast": {
|
||||
@@ -5060,9 +5074,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "5.60.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.16.tgz",
|
||||
"integrity": "sha512-V/yHdamffSS075jit+fDxaOAmdP2liok8NSNJnAZfDJErzOheuygHZEhAJrfmk5TEyM32MhkZjwo/idX791yxw==",
|
||||
"version": "5.60.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.17.tgz",
|
||||
"integrity": "sha512-AZq2FIsUHVMlp7VSe2hTfl5w4pcUkoFkM3zVsRKsn1ca8CXRDYvnin04+HP2REkwsxemuHqvDofdlhUWNpbwfw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/tern": "*"
|
||||
@@ -10007,6 +10021,15 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
|
||||
},
|
||||
"node_modules/fast-fuzzy": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-fuzzy/-/fast-fuzzy-1.12.0.tgz",
|
||||
"integrity": "sha512-sXxGgHS+ubYpsdLnvOvJ9w5GYYZrtL9mkosG3nfuD446ahvoWEsSKBP7ieGmWIKVLnaxRDgUJkZMdxRgA2Ni+Q==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"graphemesplit": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
@@ -10775,6 +10798,16 @@
|
||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
|
||||
},
|
||||
"node_modules/graphemesplit": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/graphemesplit/-/graphemesplit-2.6.0.tgz",
|
||||
"integrity": "sha512-rG9w2wAfkpg0DILa1pjnjNfucng3usON360shisqIMUBw/87pojcBSrHmeE4UwryAuBih7g8m1oilf5/u8EWdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-base64": "^3.6.0",
|
||||
"unicode-trie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/guacamole-common-js": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-1.5.0.tgz",
|
||||
@@ -11130,9 +11163,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hono": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.10.2.tgz",
|
||||
"integrity": "sha512-p6fyzl+mQo6uhESLxbF5WlBOAJMDh36PljwlKtP5V1v09NxlqGru3ShK+4wKhSuhuYf8qxMmrivHOa/M7q0sMg==",
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.10.3.tgz",
|
||||
"integrity": "sha512-2LOYWUbnhdxdL8MNbNg9XZig6k+cZXm5IjHn2Aviv7honhBMOHb+jxrKIeJRZJRmn+htUCKhaicxwXuUDlchRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
@@ -12015,6 +12048,12 @@
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.7.8",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz",
|
||||
"integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/js-levenshtein-esm": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-2.0.0.tgz",
|
||||
@@ -12245,9 +12284,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/launch-editor": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz",
|
||||
"integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz",
|
||||
"integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -12422,6 +12461,19 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/logfmt": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/logfmt/-/logfmt-1.4.0.tgz",
|
||||
"integrity": "sha512-p1Ow0C2dDJYaQBhRHt+HVMP6ELuBm4jYSYNHPMfz0J5wJ9qA6/7oBOlBZBfT1InqguTYcvJzNea5FItDxTcbyw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split": "0.2.x",
|
||||
"through": "2.3.x"
|
||||
},
|
||||
"bin": {
|
||||
"logfmt": "bin/logfmt"
|
||||
}
|
||||
},
|
||||
"node_modules/longest-streak": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
|
||||
@@ -12559,9 +12611,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mcp-proxy": {
|
||||
"version": "5.9.0",
|
||||
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-5.9.0.tgz",
|
||||
"integrity": "sha512-xonJSkuy4wmwXeykBFl0mjRMt4D0pAGCtFIfBFUz4O80VrO92ruJseIdASJO3wN6MdYHi3vbZLOQol3NsUpg4g==",
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-5.10.0.tgz",
|
||||
"integrity": "sha512-mbNKRWlh89gg8c659phTqCx6CyRseESzzvxRynFXkdRKoTLRWSn7PQKYfgGyw6j4gR2SxBod5IDGYjNSrF0UPw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mcp-proxy": "dist/bin/mcp-proxy.js"
|
||||
@@ -14629,6 +14681,12 @@
|
||||
"integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -16983,6 +17041,17 @@
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz",
|
||||
"integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw=="
|
||||
},
|
||||
"node_modules/split": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz",
|
||||
"integrity": "sha512-e0pKq+UUH2Xq/sXbYpZBZc3BawsfDZ7dgv+JtRTUPNcvF5CMR4Y9cvJqkMY0MoxWzTHvZuz1beg6pNEKlszPiQ==",
|
||||
"dependencies": {
|
||||
"through": "2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
@@ -17033,9 +17102,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/storybook": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.13.tgz",
|
||||
"integrity": "sha512-G3KZ36EVzXyHds72B/qtWiJnhUpM0xOUeYlDcO9DSHL1bDTv15cW4+upBl+mcBZrDvU838cn7Bv4GpF+O5MCfw==",
|
||||
"version": "9.1.15",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.15.tgz",
|
||||
"integrity": "sha512-es7uDdEwRVVUAt7XLAZZ1hicOq9r4ov5NFeFPpa2YEyAsyHYOCr0CTlHBfslWG6D5EVNWK3kVIIuW8GHB6hEig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
@@ -17577,6 +17646,12 @@
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"node_modules/tiny-inflate": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@@ -18069,6 +18144,16 @@
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicode-trie": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^0.2.5",
|
||||
"tiny-inflate": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unified": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
|
||||
@@ -18335,9 +18420,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.1.11",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz",
|
||||
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
|
||||
"version": "7.1.12",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
||||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
@@ -19268,7 +19353,7 @@
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2025.10.0-rc1-1760106928",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.8",
|
||||
"@rollup/plugin-commonjs": "^29.0.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.7.8",
|
||||
|
||||
@@ -115,13 +115,13 @@
|
||||
"@patternfly/elements": "^4.2.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@sentry/browser": "^10.21.0",
|
||||
"@spotlightjs/spotlight": "^4.2.0",
|
||||
"@storybook/addon-docs": "^9.1.13",
|
||||
"@storybook/addon-links": "^9.1.13",
|
||||
"@storybook/web-components": "^9.1.13",
|
||||
"@storybook/web-components-vite": "^9.1.13",
|
||||
"@types/codemirror": "^5.60.16",
|
||||
"@sentry/browser": "^10.22.0",
|
||||
"@spotlightjs/spotlight": "^4.3.0",
|
||||
"@storybook/addon-docs": "^9.1.15",
|
||||
"@storybook/addon-links": "^9.1.15",
|
||||
"@storybook/web-components": "^9.1.15",
|
||||
"@storybook/web-components-vite": "^9.1.15",
|
||||
"@types/codemirror": "^5.60.17",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.4",
|
||||
"@types/mocha": "^10.0.10",
|
||||
@@ -182,7 +182,7 @@
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.1.11",
|
||||
"vite": "^7.1.12",
|
||||
"vitest": "^3.2.4",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2025.10.0-rc1-1760106928",
|
||||
"@goauthentik/core": "^1.0.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.8",
|
||||
"@rollup/plugin-commonjs": "^29.0.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.7.8",
|
||||
|
||||
@@ -27,11 +27,12 @@ import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
|
||||
import { PageNavMenuToggle } from "#components/ak-page-navbar";
|
||||
|
||||
import type { AboutModal } from "#admin/AdminInterface/AboutModal";
|
||||
import Styles from "#admin/AdminInterface/styles.css";
|
||||
import { ROUTES } from "#admin/Routes";
|
||||
|
||||
import { CapabilitiesEnum, SessionUser, UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
|
||||
@@ -78,50 +79,13 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
//#region Styles
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
// ---
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
PFDrawer,
|
||||
PFNav,
|
||||
css`
|
||||
.pf-c-page__main {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.pf-c-page__main,
|
||||
.pf-c-drawer__content,
|
||||
.pf-c-page__drawer {
|
||||
z-index: auto !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pf-c-page {
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
|
||||
:host([theme="dark"]) {
|
||||
/* Global page background colour */
|
||||
.pf-c-page {
|
||||
--pf-c-page--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
}
|
||||
|
||||
ak-page-navbar {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.ak-sidebar {
|
||||
grid-area: nav;
|
||||
}
|
||||
|
||||
.pf-c-drawer__panel {
|
||||
z-index: var(--pf-global--ZIndex--xl);
|
||||
}
|
||||
`,
|
||||
Styles,
|
||||
];
|
||||
|
||||
//#endregion
|
||||
|
||||
37
web/src/admin/AdminInterface/styles.css
Normal file
37
web/src/admin/AdminInterface/styles.css
Normal file
@@ -0,0 +1,37 @@
|
||||
.pf-c-page__main {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.pf-c-page__main,
|
||||
.pf-c-drawer__content,
|
||||
.pf-c-page__drawer {
|
||||
z-index: auto !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pf-c-page {
|
||||
background-color: var(--pf-c-page--BackgroundColor) !important;
|
||||
}
|
||||
|
||||
:host([theme="dark"]) {
|
||||
/* Global page background colour */
|
||||
.pf-c-page {
|
||||
--pf-c-page--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
}
|
||||
|
||||
ak-page-navbar {
|
||||
grid-area: header;
|
||||
}
|
||||
|
||||
.ak-sidebar {
|
||||
grid-area: nav;
|
||||
}
|
||||
|
||||
.pf-c-drawer__panel {
|
||||
z-index: var(--pf-global--ZIndex--xl);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import "#admin/admin-overview/AdminOverviewPage";
|
||||
|
||||
import { globalAK } from "#common/global";
|
||||
|
||||
import { ID_REGEX, Route, SLUG_REGEX, UUID_REGEX } from "#elements/router/Route";
|
||||
|
||||
import { html } from "lit";
|
||||
@@ -158,3 +160,14 @@ export const ROUTES: Route[] = [
|
||||
return html`<ak-enterprise-license-list></ak-enterprise-license-list>`;
|
||||
}),
|
||||
];
|
||||
|
||||
/**
|
||||
* Application route helpers.
|
||||
*
|
||||
* @TODO: This API isn't quite right yet. Revisit after the hash router is replaced.
|
||||
*/
|
||||
export const ApplicationRoute = {
|
||||
EditURL(slug: string, base = globalAK().api.base) {
|
||||
return `${base}if/admin/#/core/applications/${slug}`;
|
||||
},
|
||||
} as const;
|
||||
|
||||
5
web/src/admin/admin-overview/cards/RecentEventsCard.css
Normal file
5
web/src/admin/admin-overview/cards/RecentEventsCard.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.pf-c-card__title {
|
||||
--pf-c-card__title--FontFamily: var(--pf-global--FontFamily--heading--sans-serif);
|
||||
--pf-c-card__title--FontSize: var(--pf-global--FontSize--md);
|
||||
--pf-c-card__title--FontWeight: var(--pf-global--FontWeight--bold);
|
||||
}
|
||||
@@ -12,12 +12,13 @@ import { actionToLabel } from "#common/labels";
|
||||
import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import Styles from "#admin/admin-overview/cards/RecentEventsCard.css";
|
||||
import { EventGeo, renderEventUser } from "#admin/events/utils";
|
||||
|
||||
import { Event, EventsApi } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, CSSResult, html, TemplateResult } from "lit";
|
||||
import { CSSResult, html, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
@@ -42,15 +43,10 @@ export class RecentEventsCard extends Table<Event> {
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
// ---
|
||||
...super.styles,
|
||||
PFCard,
|
||||
css`
|
||||
.pf-c-card__title {
|
||||
--pf-c-card__title--FontFamily: var(--pf-global--FontFamily--heading--sans-serif);
|
||||
--pf-c-card__title--FontSize: var(--pf-global--FontSize--md);
|
||||
--pf-c-card__title--FontWeight: var(--pf-global--FontWeight--bold);
|
||||
}
|
||||
`,
|
||||
Styles,
|
||||
];
|
||||
|
||||
protected override rowLabel(item: Event): string {
|
||||
|
||||
15
web/src/admin/admin-overview/cards/VersionStatusCard.css
Normal file
15
web/src/admin/admin-overview/cards/VersionStatusCard.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.pf-c-card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
}
|
||||
}
|
||||
|
||||
.status-container {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--icon--FontSize--md);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,21 @@
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
|
||||
import { AdminStatus, AdminStatusCard } from "#admin/admin-overview/cards/AdminStatusCard";
|
||||
import Styles from "#admin/admin-overview/cards/VersionStatusCard.css";
|
||||
|
||||
import { AdminApi, Version } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, html, TemplateResult } from "lit";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-version")
|
||||
export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
public static styles = [...super.styles, Styles];
|
||||
|
||||
public override icon = "pf-icon pf-icon-bundle";
|
||||
public override label = msg("Version");
|
||||
|
||||
static styles = [
|
||||
...super.styles,
|
||||
// HACK: Fixes Lit Analyzer's outdated parser.
|
||||
(css as typeof css) /*css*/ `
|
||||
.pf-c-card {
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.pf-c-card__title {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
}
|
||||
}
|
||||
|
||||
.status-container {
|
||||
@container (width < 200px) {
|
||||
font-size: var(--pf-global--icon--FontSize--md);
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
getPrimaryValue(): Promise<Version> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import "#elements/forms/DeleteBulkForm";
|
||||
import "#elements/forms/ModalForm";
|
||||
import "#elements/tasks/TaskList";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
import "#elements/ak-mdx/ak-mdx";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
@@ -44,6 +45,16 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
|
||||
return msg("Unknown");
|
||||
}
|
||||
|
||||
const BlueprintDescriptionProperty = "blueprints.goauthentik.io/description";
|
||||
|
||||
export function formatBlueprintDescription(item: BlueprintInstance): string | null {
|
||||
const { labels = {} } = (item.metadata || {}) as {
|
||||
labels?: Record<string, string | undefined>;
|
||||
};
|
||||
|
||||
return labels[BlueprintDescriptionProperty] || null;
|
||||
}
|
||||
|
||||
@customElement("ak-blueprint-list")
|
||||
export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
protected override searchEnabled = true;
|
||||
@@ -133,18 +144,13 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
}
|
||||
|
||||
row(item: BlueprintInstance): SlottedTemplateResult[] {
|
||||
let description = undefined;
|
||||
const descKey = "blueprints.goauthentik.io/description";
|
||||
if (
|
||||
item.metadata &&
|
||||
item.metadata.labels &&
|
||||
Object.hasOwn(item.metadata?.labels, descKey)
|
||||
) {
|
||||
description = item.metadata?.labels[descKey];
|
||||
}
|
||||
const description = formatBlueprintDescription(item);
|
||||
|
||||
return [
|
||||
html`<div>${item.name}</div>
|
||||
${description ? html`<small>${description}</small>` : nothing}`,
|
||||
${description
|
||||
? html`<small><ak-mdx .content=${description}></ak-mdx></small>`
|
||||
: nothing}`,
|
||||
html`${BlueprintStatus(item)}`,
|
||||
Timestamp(item.lastApplied),
|
||||
html`<ak-status-label ?good=${item.enabled}></ak-status-label>`,
|
||||
|
||||
@@ -78,11 +78,9 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
|
||||
row(item: Flow): SlottedTemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<a href="#/flow/flows/${item.slug}">
|
||||
<code>${item.slug}</code>
|
||||
</a>
|
||||
</div>
|
||||
html`<a href="#/flow/flows/${item.slug}" class="pf-m-block">
|
||||
<code>${item.slug}</code>
|
||||
</a>
|
||||
<small>${item.title}</small>`,
|
||||
html`${item.name}`,
|
||||
html`${Array.from(item.stages || []).length}`,
|
||||
|
||||
@@ -8,6 +8,7 @@ import "#elements/Tabs";
|
||||
import "#elements/buttons/ActionButton/index";
|
||||
import "#elements/buttons/SpinnerButton/index";
|
||||
import "#elements/forms/ModalForm";
|
||||
import "#elements/ak-mdx/ak-mdx";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
@@ -159,8 +160,10 @@ export class GroupViewPage extends AKElement {
|
||||
>
|
||||
<div class="pf-c-card__title">${msg("Notes")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${Object.hasOwn(this.group?.attributes || {}, "notes")
|
||||
? html`${this.group.attributes?.notes}`
|
||||
${this.group?.attributes?.notes
|
||||
? html`<ak-mdx
|
||||
.content=${this.group.attributes.notes}
|
||||
></ak-mdx>`
|
||||
: html`
|
||||
<p>
|
||||
${msg(
|
||||
|
||||
@@ -63,7 +63,7 @@ export class ServiceConnectionWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}-${type.modelName}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form type=${type.component}></ak-proxy-form>
|
||||
</ak-wizard-page-form>
|
||||
|
||||
@@ -87,7 +87,7 @@ export class PolicyWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}-${type.modelName}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form type=${type.component}></ak-proxy-form>
|
||||
</ak-wizard-page-form>
|
||||
@@ -96,7 +96,7 @@ export class PolicyWizard extends AKElement {
|
||||
${this.showBindingPage
|
||||
? html`<ak-wizard-page-form
|
||||
slot="create-binding"
|
||||
.sidebarLabel=${() => msg("Create Binding")}
|
||||
label=${msg("Create Binding")}
|
||||
.activePageCallback=${async (context: FormWizardPage) => {
|
||||
const createSlot = context.host.steps[1];
|
||||
const bindingForm =
|
||||
|
||||
@@ -87,7 +87,9 @@ export class EventMatcherPolicyForm extends BasePolicyForm<EventMatcherPolicy> {
|
||||
DEFAULT_CONFIG,
|
||||
).eventsEventsActionsList();
|
||||
return items.filter((item) =>
|
||||
query ? item.name.includes(query) : true,
|
||||
query
|
||||
? item.name.toLowerCase().includes(query.toLowerCase())
|
||||
: true,
|
||||
);
|
||||
}}
|
||||
.renderElement=${(item: TypeCreate): string => {
|
||||
|
||||
@@ -74,7 +74,7 @@ export class PropertyMappingWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}-${type.modelName}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form type=${type.component}></ak-proxy-form>
|
||||
</ak-wizard-page-form>
|
||||
|
||||
@@ -72,7 +72,7 @@ export class ProviderWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form type=${type.component}></ak-proxy-form>
|
||||
</ak-wizard-page-form>
|
||||
|
||||
@@ -52,7 +52,8 @@ export function renderForm({ provider = {}, errors = {}, brand }: LDAPProviderFo
|
||||
return html`
|
||||
<ak-text-input
|
||||
name="name"
|
||||
placeholder=${msg("Provider name...")}
|
||||
placeholder=${msg("Type a provider name...")}
|
||||
autocomplete="off"
|
||||
value=${ifDefined(provider.name)}
|
||||
label=${msg("Name")}
|
||||
.errorMessages=${errors.name}
|
||||
|
||||
@@ -149,7 +149,8 @@ export function renderForm({
|
||||
}: OAuth2ProviderFormProps) {
|
||||
return html` <ak-text-input
|
||||
name="name"
|
||||
placeholder=${msg("Provider name...")}
|
||||
placeholder=${msg("Type a provider name...")}
|
||||
autocomplete="off"
|
||||
label=${msg("Provider Name")}
|
||||
value=${ifDefined(provider.name)}
|
||||
.errorMessages=${errors.name}
|
||||
@@ -231,6 +232,7 @@ export function renderForm({
|
||||
name="logoutUri"
|
||||
value="${provider?.logoutUri ?? ""}"
|
||||
input-hint="code"
|
||||
inputmode="url"
|
||||
placeholder="https://..."
|
||||
.help=${msg(
|
||||
"URI to send logout notifications to when users log out. Required for OpenID Connect Logout functionality.",
|
||||
@@ -265,15 +267,6 @@ export function renderForm({
|
||||
></ak-crypto-certificate-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Key used to sign the tokens.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Encryption Key")} name="encryptionKey">
|
||||
<!-- NOTE: 'null' cast to 'undefined' on encryptionKey to satisfy Lit requirements -->
|
||||
<ak-crypto-certificate-search
|
||||
label=${msg("Encryption Key")}
|
||||
placeholder=${msg("Select an encryption key...")}
|
||||
certificate=${ifPresent(provider.encryptionKey)}
|
||||
></ak-crypto-certificate-search>
|
||||
<p class="pf-c-form__helper-text">${msg("Key used to encrypt the tokens.")}</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
|
||||
@@ -382,6 +375,23 @@ export function renderForm({
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Encryption Key")} name="encryptionKey">
|
||||
<!-- NOTE: 'null' cast to 'undefined' on encryptionKey to satisfy Lit requirements -->
|
||||
<ak-crypto-certificate-search
|
||||
label=${msg("Encryption Key")}
|
||||
placeholder=${msg("Select an encryption key...")}
|
||||
certificate=${ifPresent(provider.encryptionKey)}
|
||||
></ak-crypto-certificate-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Key used to encrypt the tokens. Only enable this if the application using this provider supports JWE tokens.",
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("authentik only supports RSA-OAEP-256 for encryption.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
|
||||
<ak-radio-input
|
||||
name="subMode"
|
||||
label=${msg("Subject mode")}
|
||||
|
||||
@@ -51,7 +51,8 @@ export function renderForm({ provider = {}, errors = {}, brand }: RADIUSProvider
|
||||
<ak-text-input
|
||||
name="name"
|
||||
label=${msg("Provider Name")}
|
||||
placeholder=${msg("Provider name...")}
|
||||
placeholder=${msg("Type a provider name...")}
|
||||
autocomplete="off"
|
||||
value=${ifDefined(provider.name)}
|
||||
.errorMessages=${errors.name}
|
||||
required
|
||||
|
||||
@@ -256,6 +256,7 @@ export function renderForm({
|
||||
<ak-crypto-certificate-search
|
||||
.certificate=${provider.signingKp}
|
||||
@input=${setHasSigningKp}
|
||||
singleton
|
||||
></ak-crypto-certificate-search>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
|
||||
@@ -17,7 +17,7 @@ export class SCIMProviderFormPage extends BaseProviderForm<SCIMProvider> {
|
||||
}
|
||||
|
||||
async send(data: SCIMProvider): Promise<SCIMProvider> {
|
||||
if (this.instance) {
|
||||
if (this.instance?.pk) {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersScimUpdate({
|
||||
id: this.instance.pk,
|
||||
sCIMProviderRequest: data,
|
||||
|
||||
@@ -66,7 +66,7 @@ export class SourceWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}-${type.modelName}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form
|
||||
.args=${{
|
||||
|
||||
@@ -101,7 +101,7 @@ export class StageWizard extends AKElement {
|
||||
return html`
|
||||
<ak-wizard-page-form
|
||||
slot=${`type-${type.component}-${type.modelName}`}
|
||||
.sidebarLabel=${() => msg(str`Create ${type.name}`)}
|
||||
label=${msg(str`Create ${type.name}`)}
|
||||
>
|
||||
<ak-proxy-form type=${type.component}></ak-proxy-form>
|
||||
</ak-wizard-page-form>
|
||||
@@ -110,7 +110,7 @@ export class StageWizard extends AKElement {
|
||||
${this.showBindingPage
|
||||
? html`<ak-wizard-page-form
|
||||
slot="create-binding"
|
||||
.sidebarLabel=${() => msg("Create Binding")}
|
||||
label=${msg("Create Binding")}
|
||||
.activePageCallback=${async (context: FormWizardPage) => {
|
||||
const createSlot = context.host.steps[1];
|
||||
const bindingForm =
|
||||
|
||||
@@ -23,6 +23,7 @@ import "#elements/user/UserConsentList";
|
||||
import "#elements/user/UserReputationList";
|
||||
import "#elements/user/sources/SourceSettings";
|
||||
import "./UserDevicesTable.js";
|
||||
import "#elements/ak-mdx/ak-mdx";
|
||||
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
@@ -420,8 +421,8 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
|
||||
>
|
||||
<div class="pf-c-card__title">${msg("Notes")}</div>
|
||||
<div class="pf-c-card__body">
|
||||
${Object.hasOwn(this.user?.attributes || {}, "notes")
|
||||
? html`${this.user.attributes?.notes}`
|
||||
${this.user?.attributes?.notes
|
||||
? html`<ak-mdx .content=${this.user.attributes.notes}></ak-mdx>`
|
||||
: html`
|
||||
<p>
|
||||
${msg(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user