Compare commits
3 Commits
version/20
...
docs-certs
Author | SHA1 | Date | |
---|---|---|---|
d660a392b9 | |||
f530ce5e02 | |||
d4012df59d |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2024.8.0
|
||||
current_version = 2024.6.3
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
|
@ -29,9 +29,9 @@ outputs:
|
||||
imageTags:
|
||||
description: "Docker image tags"
|
||||
value: ${{ steps.ev.outputs.imageTags }}
|
||||
attestImageNames:
|
||||
description: "Docker image names used for attestation"
|
||||
value: ${{ steps.ev.outputs.attestImageNames }}
|
||||
imageNames:
|
||||
description: "Docker image names"
|
||||
value: ${{ steps.ev.outputs.imageNames }}
|
||||
imageMainTag:
|
||||
description: "Docker image main tag"
|
||||
value: ${{ steps.ev.outputs.imageMainTag }}
|
||||
|
@ -51,24 +51,15 @@ else:
|
||||
]
|
||||
|
||||
image_main_tag = image_tags[0].split(":")[-1]
|
||||
|
||||
|
||||
def get_attest_image_names(image_with_tags: list[str]):
|
||||
"""Attestation only for GHCR"""
|
||||
image_tags = []
|
||||
for image_name in set(name.split(":")[0] for name in image_with_tags):
|
||||
if not image_name.startswith("ghcr.io"):
|
||||
continue
|
||||
image_tags.append(image_name)
|
||||
return ",".join(set(image_tags))
|
||||
|
||||
image_tags_rendered = ",".join(image_tags)
|
||||
image_names_rendered = ",".join(set(name.split(":")[0] for name in image_tags))
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
|
||||
print(f"shouldBuild={should_build}", file=_output)
|
||||
print(f"sha={sha}", file=_output)
|
||||
print(f"version={version}", file=_output)
|
||||
print(f"prerelease={prerelease}", file=_output)
|
||||
print(f"imageTags={','.join(image_tags)}", file=_output)
|
||||
print(f"attestImageNames={get_attest_image_names(image_tags)}", file=_output)
|
||||
print(f"imageTags={image_tags_rendered}", file=_output)
|
||||
print(f"imageNames={image_names_rendered}", file=_output)
|
||||
print(f"imageMainTag={image_main_tag}", file=_output)
|
||||
print(f"imageMainName={image_tags[0]}", file=_output)
|
||||
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -58,10 +58,6 @@ updates:
|
||||
patterns:
|
||||
- "@rollup/*"
|
||||
- "rollup-*"
|
||||
swc:
|
||||
patterns:
|
||||
- "@swc/*"
|
||||
- "swc-*"
|
||||
wdio:
|
||||
patterns:
|
||||
- "@wdio/*"
|
||||
|
2
.github/workflows/ci-main.yml
vendored
2
.github/workflows/ci-main.yml
vendored
@ -261,7 +261,7 @@ jobs:
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-name: ${{ steps.ev.outputs.imageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
pr-comment:
|
||||
|
4
.github/workflows/ci-outpost.yml
vendored
4
.github/workflows/ci-outpost.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
version: v1.54.2
|
||||
args: --timeout 5000s --verbose
|
||||
skip-cache: true
|
||||
test-unittest:
|
||||
@ -115,7 +115,7 @@ jobs:
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-name: ${{ steps.ev.outputs.imageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
build-binary:
|
||||
|
2
.github/workflows/ci-web.yml
vendored
2
.github/workflows/ci-web.yml
vendored
@ -92,4 +92,4 @@ jobs:
|
||||
run: make gen-client-ts
|
||||
- name: test
|
||||
working-directory: web/
|
||||
run: npm run test || exit 0
|
||||
run: npm run test
|
||||
|
8
.github/workflows/release-publish.yml
vendored
8
.github/workflows/release-publish.yml
vendored
@ -51,14 +51,12 @@ jobs:
|
||||
secrets: |
|
||||
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
|
||||
GEOIPUPDATE_LICENSE_KEY=${{ secrets.GEOIPUPDATE_LICENSE_KEY }}
|
||||
build-args: |
|
||||
VERSION=${{ github.ref }}
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- uses: actions/attest-build-provenance@v1
|
||||
id: attest
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-name: ${{ steps.ev.outputs.imageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
build-outpost:
|
||||
@ -113,8 +111,6 @@ jobs:
|
||||
id: push
|
||||
with:
|
||||
push: true
|
||||
build-args: |
|
||||
VERSION=${{ github.ref }}
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: ${{ matrix.type }}.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@ -122,7 +118,7 @@ jobs:
|
||||
- uses: actions/attest-build-provenance@v1
|
||||
id: attest
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-name: ${{ steps.ev.outputs.imageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
build-outpost-binary:
|
||||
|
23
Dockerfile
23
Dockerfile
@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build website
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS website-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 as website-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@ -20,7 +20,7 @@ COPY ./SECURITY.md /work/
|
||||
RUN npm run build-bundled
|
||||
|
||||
# Stage 2: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 AS web-builder
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/library/node:22 as web-builder
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
@ -43,7 +43,7 @@ COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: Build go proxy
|
||||
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS go-builder
|
||||
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS go-builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@ -80,7 +80,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
go build -o /go/authentik ./cmd/server
|
||||
|
||||
# Stage 4: MaxMind GeoIP
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 AS geoip
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0.1 as geoip
|
||||
|
||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
||||
ENV GEOIPUPDATE_VERBOSE="1"
|
||||
@ -96,9 +96,6 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
# Stage 5: Python dependencies
|
||||
FROM ghcr.io/goauthentik/fips-python:3.12.5-slim-bookworm-fips-full AS python-deps
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
|
||||
WORKDIR /ak-root/poetry
|
||||
|
||||
ENV VENV_PATH="/ak-root/venv" \
|
||||
@ -126,15 +123,15 @@ RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
|
||||
# Stage 6: Run
|
||||
FROM ghcr.io/goauthentik/fips-python:3.12.5-slim-bookworm-fips-full AS final-image
|
||||
|
||||
ARG VERSION
|
||||
ARG GIT_BUILD_HASH
|
||||
ARG VERSION
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.url=https://goauthentik.io
|
||||
LABEL org.opencontainers.image.description="goauthentik.io Main server image, see https://goauthentik.io for more info."
|
||||
LABEL org.opencontainers.image.source=https://github.com/goauthentik/authentik
|
||||
LABEL org.opencontainers.image.version=${VERSION}
|
||||
LABEL org.opencontainers.image.revision=${GIT_BUILD_HASH}
|
||||
LABEL org.opencontainers.image.url https://goauthentik.io
|
||||
LABEL org.opencontainers.image.description goauthentik.io Main server image, see https://goauthentik.io for more info.
|
||||
LABEL org.opencontainers.image.source https://github.com/goauthentik/authentik
|
||||
LABEL org.opencontainers.image.version ${VERSION}
|
||||
LABEL org.opencontainers.image.revision ${GIT_BUILD_HASH}
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
7
Makefile
7
Makefile
@ -43,7 +43,7 @@ help: ## Show this help
|
||||
sort
|
||||
@echo ""
|
||||
|
||||
go-test:
|
||||
test-go:
|
||||
go test -timeout 0 -v -race -cover ./...
|
||||
|
||||
test-docker: ## Run all tests in a docker-compose
|
||||
@ -205,14 +205,11 @@ gen: gen-build gen-client-ts
|
||||
web-build: web-install ## Build the Authentik UI
|
||||
cd web && npm run build
|
||||
|
||||
web: web-lint-fix web-lint web-check-compile web-test ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
|
||||
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
|
||||
|
||||
web-install: ## Install the necessary libraries to build the Authentik UI
|
||||
cd web && npm ci
|
||||
|
||||
web-test: ## Run tests for the Authentik UI
|
||||
cd web && npm run test
|
||||
|
||||
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
|
||||
rm -rf web/dist/
|
||||
mkdir web/dist/
|
||||
|
@ -15,9 +15,7 @@
|
||||
|
||||
## What is authentik?
|
||||
|
||||
authentik is an open-source Identity Provider that emphasizes flexibility and versatility, with support for a wide set of protocols.
|
||||
|
||||
Our [enterprise offer](https://goauthentik.io/pricing) can also be used as a self-hosted replacement for large-scale deployments of Okta/Auth0, Entra ID, Ping Identity, or other legacy IdPs for employees and B2B2C use.
|
||||
authentik is an open-source Identity Provider that emphasizes flexibility and versatility. It can be seamlessly integrated into existing environments to support new protocols. authentik is also a great solution for implementing sign-up, recovery, and other similar features in your application, saving you the hassle of dealing with them.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2024.8.0"
|
||||
__version__ = "2024.6.3"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -12,7 +12,6 @@ from rest_framework.views import APIView
|
||||
from authentik import __version__, get_build_hash
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.outposts.models import Outpost
|
||||
|
||||
|
||||
class VersionSerializer(PassiveSerializer):
|
||||
@ -23,7 +22,6 @@ class VersionSerializer(PassiveSerializer):
|
||||
version_latest_valid = SerializerMethodField()
|
||||
build_hash = SerializerMethodField()
|
||||
outdated = SerializerMethodField()
|
||||
outpost_outdated = SerializerMethodField()
|
||||
|
||||
def get_build_hash(self, _) -> str:
|
||||
"""Get build hash, if version is not latest or released"""
|
||||
@ -49,15 +47,6 @@ class VersionSerializer(PassiveSerializer):
|
||||
"""Check if we're running the latest version"""
|
||||
return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
|
||||
|
||||
def get_outpost_outdated(self, _) -> bool:
|
||||
"""Check if any outpost is outdated/has a version mismatch"""
|
||||
any_outdated = False
|
||||
for outpost in Outpost.objects.all():
|
||||
for state in outpost.state:
|
||||
if state.version_outdated:
|
||||
any_outdated = True
|
||||
return any_outdated
|
||||
|
||||
|
||||
class VersionView(APIView):
|
||||
"""Get running and latest version."""
|
||||
|
@ -14,7 +14,6 @@ from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
|
||||
class DeleteAction(Enum):
|
||||
@ -54,7 +53,7 @@ class UsedByMixin:
|
||||
@extend_schema(
|
||||
responses={200: UsedBySerializer(many=True)},
|
||||
)
|
||||
@action(detail=True, pagination_class=None, filter_backends=[ObjectFilter])
|
||||
@action(detail=True, pagination_class=None, filter_backends=[])
|
||||
def used_by(self, request: Request, *args, **kwargs) -> Response:
|
||||
"""Get a list of all objects that use this object"""
|
||||
model: Model = self.get_object()
|
||||
|
@ -35,7 +35,6 @@ from authentik.crypto.builder import CertificateBuilder, PrivateKeyAlg
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@ -266,7 +265,7 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
||||
],
|
||||
responses={200: CertificateDataSerializer(many=False)},
|
||||
)
|
||||
@action(detail=True, pagination_class=None, filter_backends=[ObjectFilter])
|
||||
@action(detail=True, pagination_class=None, filter_backends=[])
|
||||
def view_certificate(self, request: Request, pk: str) -> Response:
|
||||
"""Return certificate-key pairs certificate and log access"""
|
||||
certificate: CertificateKeyPair = self.get_object()
|
||||
@ -296,7 +295,7 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
||||
],
|
||||
responses={200: CertificateDataSerializer(many=False)},
|
||||
)
|
||||
@action(detail=True, pagination_class=None, filter_backends=[ObjectFilter])
|
||||
@action(detail=True, pagination_class=None, filter_backends=[])
|
||||
def view_private_key(self, request: Request, pk: str) -> Response:
|
||||
"""Return certificate-key pairs private key and log access"""
|
||||
certificate: CertificateKeyPair = self.get_object()
|
||||
|
@ -214,46 +214,6 @@ class TestCrypto(APITestCase):
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertIn("Content-Disposition", response)
|
||||
|
||||
def test_certificate_download_denied(self):
|
||||
"""Test certificate export (download)"""
|
||||
self.client.logout()
|
||||
keypair = create_test_cert()
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:certificatekeypair-view-certificate",
|
||||
kwargs={"pk": keypair.pk},
|
||||
)
|
||||
)
|
||||
self.assertEqual(403, response.status_code)
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:certificatekeypair-view-certificate",
|
||||
kwargs={"pk": keypair.pk},
|
||||
),
|
||||
data={"download": True},
|
||||
)
|
||||
self.assertEqual(403, response.status_code)
|
||||
|
||||
def test_private_key_download_denied(self):
|
||||
"""Test private_key export (download)"""
|
||||
self.client.logout()
|
||||
keypair = create_test_cert()
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:certificatekeypair-view-private-key",
|
||||
kwargs={"pk": keypair.pk},
|
||||
)
|
||||
)
|
||||
self.assertEqual(403, response.status_code)
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:certificatekeypair-view-private-key",
|
||||
kwargs={"pk": keypair.pk},
|
||||
),
|
||||
data={"download": True},
|
||||
)
|
||||
self.assertEqual(403, response.status_code)
|
||||
|
||||
def test_used_by(self):
|
||||
"""Test used_by endpoint"""
|
||||
self.client.force_login(create_test_admin_user())
|
||||
@ -286,26 +246,6 @@ class TestCrypto(APITestCase):
|
||||
],
|
||||
)
|
||||
|
||||
def test_used_by_denied(self):
|
||||
"""Test used_by endpoint"""
|
||||
self.client.logout()
|
||||
keypair = create_test_cert()
|
||||
OAuth2Provider.objects.create(
|
||||
name=generate_id(),
|
||||
client_id="test",
|
||||
client_secret=generate_key(),
|
||||
authorization_flow=create_test_flow(),
|
||||
redirect_uris="http://localhost",
|
||||
signing_key=keypair,
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:certificatekeypair-used-by",
|
||||
kwargs={"pk": keypair.pk},
|
||||
)
|
||||
)
|
||||
self.assertEqual(403, response.status_code)
|
||||
|
||||
def test_discovery(self):
|
||||
"""Test certificate discovery"""
|
||||
name = generate_id()
|
||||
|
@ -1,11 +1,12 @@
|
||||
"""Enterprise API Views"""
|
||||
|
||||
from dataclasses import asdict
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema, inline_serializer
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, IntegerField
|
||||
@ -86,7 +87,7 @@ class LicenseViewSet(UsedByMixin, ModelViewSet):
|
||||
},
|
||||
)
|
||||
@action(detail=False, methods=["GET"])
|
||||
def install_id(self, request: Request) -> Response:
|
||||
def get_install_id(self, request: Request) -> Response:
|
||||
"""Get install_id"""
|
||||
return Response(
|
||||
data={
|
||||
@ -99,22 +100,12 @@ class LicenseViewSet(UsedByMixin, ModelViewSet):
|
||||
responses={
|
||||
200: LicenseSummarySerializer(),
|
||||
},
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="cached",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
default=True,
|
||||
)
|
||||
],
|
||||
)
|
||||
@action(detail=False, methods=["GET"], permission_classes=[IsAuthenticated])
|
||||
def summary(self, request: Request) -> Response:
|
||||
"""Get the total license status"""
|
||||
summary = LicenseKey.cached_summary()
|
||||
if request.query_params.get("cached", "true").lower() == "false":
|
||||
summary = LicenseKey.get_total().summary()
|
||||
response = LicenseSummarySerializer(instance=summary)
|
||||
response = LicenseSummarySerializer(data=asdict(LicenseKey.cached_summary()))
|
||||
response.is_valid(raise_exception=True)
|
||||
return Response(response.data)
|
||||
|
||||
@permission_required(None, ["authentik_enterprise.view_license"])
|
||||
|
@ -25,4 +25,4 @@ class AuthentikEnterpriseConfig(EnterpriseConfig):
|
||||
"""Actual enterprise check, cached"""
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
|
||||
return LicenseKey.cached_summary().status.is_valid
|
||||
return LicenseKey.cached_summary().status
|
||||
|
@ -20,7 +20,6 @@ from rest_framework.fields import (
|
||||
ChoiceField,
|
||||
DateTimeField,
|
||||
IntegerField,
|
||||
ListField,
|
||||
)
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
@ -56,7 +55,6 @@ class LicenseFlags(Enum):
|
||||
"""License flags"""
|
||||
|
||||
TRIAL = "trial"
|
||||
NON_PRODUCTION = "non_production"
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -67,7 +65,6 @@ class LicenseSummary:
|
||||
external_users: int
|
||||
status: LicenseUsageStatus
|
||||
latest_valid: datetime
|
||||
license_flags: list[LicenseFlags]
|
||||
|
||||
|
||||
class LicenseSummarySerializer(PassiveSerializer):
|
||||
@ -77,7 +74,6 @@ class LicenseSummarySerializer(PassiveSerializer):
|
||||
external_users = IntegerField(required=True)
|
||||
status = ChoiceField(choices=LicenseUsageStatus.choices)
|
||||
latest_valid = DateTimeField()
|
||||
license_flags = ListField(child=ChoiceField(choices=tuple(x.value for x in LicenseFlags)))
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -90,7 +86,7 @@ class LicenseKey:
|
||||
name: str
|
||||
internal_users: int = 0
|
||||
external_users: int = 0
|
||||
license_flags: list[LicenseFlags] = field(default_factory=list)
|
||||
flags: list[LicenseFlags] = field(default_factory=list)
|
||||
|
||||
@staticmethod
|
||||
def validate(jwt: str, check_expiry=True) -> "LicenseKey":
|
||||
@ -117,7 +113,7 @@ class LicenseKey:
|
||||
our_cert.public_key(),
|
||||
algorithms=["ES512"],
|
||||
audience=get_license_aud(),
|
||||
options={"verify_exp": check_expiry, "verify_signature": check_expiry},
|
||||
options={"verify_exp": check_expiry},
|
||||
),
|
||||
)
|
||||
except PyJWTError:
|
||||
@ -134,8 +130,9 @@ class LicenseKey:
|
||||
exp_ts = int(mktime(lic.expiry.timetuple()))
|
||||
if total.exp == 0:
|
||||
total.exp = exp_ts
|
||||
total.exp = max(total.exp, exp_ts)
|
||||
total.license_flags.extend(lic.status.license_flags)
|
||||
if exp_ts <= total.exp:
|
||||
total.exp = exp_ts
|
||||
total.flags.extend(lic.status.flags)
|
||||
return total
|
||||
|
||||
@staticmethod
|
||||
@ -219,7 +216,6 @@ class LicenseKey:
|
||||
internal_users=self.internal_users,
|
||||
external_users=self.external_users,
|
||||
status=status,
|
||||
license_flags=self.license_flags,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
@ -6,10 +6,7 @@ from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider
|
||||
from authentik.enterprise.providers.google_workspace.tasks import (
|
||||
google_workspace_sync,
|
||||
google_workspace_sync_objects,
|
||||
)
|
||||
from authentik.enterprise.providers.google_workspace.tasks import google_workspace_sync
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
|
||||
|
||||
|
||||
@ -55,4 +52,3 @@ class GoogleWorkspaceProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixi
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
sync_single_task = google_workspace_sync
|
||||
sync_objects_task = google_workspace_sync_objects
|
||||
|
@ -181,7 +181,7 @@ class GoogleWorkspaceProviderMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-google-workspace-form"
|
||||
return "ak-property-mapping-google-workspace-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -6,10 +6,7 @@ from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
|
||||
from authentik.enterprise.providers.microsoft_entra.tasks import (
|
||||
microsoft_entra_sync,
|
||||
microsoft_entra_sync_objects,
|
||||
)
|
||||
from authentik.enterprise.providers.microsoft_entra.tasks import microsoft_entra_sync
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
|
||||
|
||||
|
||||
@ -53,4 +50,3 @@ class MicrosoftEntraProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
sync_single_task = microsoft_entra_sync
|
||||
sync_objects_task = microsoft_entra_sync_objects
|
||||
|
@ -170,7 +170,7 @@ class MicrosoftEntraProviderMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-microsoft-entra-form"
|
||||
return "ak-property-mapping-microsoft-entra-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-12 12:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_rac", "0004_alter_connectiontoken_expires"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="racpropertymapping",
|
||||
options={
|
||||
"verbose_name": "RAC Provider Property Mapping",
|
||||
"verbose_name_plural": "RAC Provider Property Mappings",
|
||||
},
|
||||
),
|
||||
]
|
@ -125,7 +125,7 @@ class RACPropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-rac-form"
|
||||
return "ak-property-mapping-rac-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
@ -136,8 +136,8 @@ class RACPropertyMapping(PropertyMapping):
|
||||
return RACPropertyMappingSerializer
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("RAC Provider Property Mapping")
|
||||
verbose_name_plural = _("RAC Provider Property Mappings")
|
||||
verbose_name = _("RAC Property Mapping")
|
||||
verbose_name_plural = _("RAC Property Mappings")
|
||||
|
||||
|
||||
class ConnectionToken(ExpiringModel):
|
||||
|
@ -44,7 +44,7 @@ websocket_urlpatterns = [
|
||||
|
||||
api_urlpatterns = [
|
||||
("providers/rac", RACProviderViewSet),
|
||||
("propertymappings/provider/rac", RACPropertyMappingViewSet),
|
||||
("propertymappings/rac", RACPropertyMappingViewSet),
|
||||
("rac/endpoints", EndpointViewSet),
|
||||
("rac/connection_tokens", ConnectionTokenViewSet),
|
||||
]
|
||||
|
@ -37,7 +37,6 @@ from authentik.lib.utils.file import (
|
||||
)
|
||||
from authentik.lib.views import bad_request_message
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@ -282,7 +281,7 @@ class FlowViewSet(UsedByMixin, ModelViewSet):
|
||||
400: OpenApiResponse(description="Flow not applicable"),
|
||||
},
|
||||
)
|
||||
@action(detail=True, pagination_class=None, filter_backends=[ObjectFilter])
|
||||
@action(detail=True, pagination_class=None, filter_backends=[])
|
||||
def execute(self, request: Request, slug: str):
|
||||
"""Execute flow for current user"""
|
||||
# Because we pre-plan the flow here, and not in the planner, we need to manually clear
|
||||
|
@ -1,19 +1,16 @@
|
||||
from celery import Task
|
||||
from collections.abc import Callable
|
||||
|
||||
from django.utils.text import slugify
|
||||
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField
|
||||
from rest_framework.fields import BooleanField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.events.api.tasks import SystemTaskSerializer
|
||||
from authentik.events.logs import LogEvent, LogEventSerializer
|
||||
from authentik.lib.sync.outgoing.models import OutgoingSyncProvider
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
|
||||
class SyncStatusSerializer(PassiveSerializer):
|
||||
@ -23,29 +20,10 @@ class SyncStatusSerializer(PassiveSerializer):
|
||||
tasks = SystemTaskSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class SyncObjectSerializer(PassiveSerializer):
|
||||
"""Sync object serializer"""
|
||||
|
||||
sync_object_model = ChoiceField(
|
||||
choices=(
|
||||
(class_to_path(User), "user"),
|
||||
(class_to_path(Group), "group"),
|
||||
)
|
||||
)
|
||||
sync_object_id = CharField()
|
||||
|
||||
|
||||
class SyncObjectResultSerializer(PassiveSerializer):
|
||||
"""Result of a single object sync"""
|
||||
|
||||
messages = LogEventSerializer(many=True, read_only=True)
|
||||
|
||||
|
||||
class OutgoingSyncProviderStatusMixin:
|
||||
"""Common API Endpoints for Outgoing sync providers"""
|
||||
|
||||
sync_single_task: type[Task] = None
|
||||
sync_objects_task: type[Task] = None
|
||||
sync_single_task: Callable = None
|
||||
|
||||
@extend_schema(
|
||||
responses={
|
||||
@ -58,7 +36,7 @@ class OutgoingSyncProviderStatusMixin:
|
||||
detail=True,
|
||||
pagination_class=None,
|
||||
url_path="sync/status",
|
||||
filter_backends=[ObjectFilter],
|
||||
filter_backends=[],
|
||||
)
|
||||
def sync_status(self, request: Request, pk: int) -> Response:
|
||||
"""Get provider's sync status"""
|
||||
@ -77,30 +55,6 @@ class OutgoingSyncProviderStatusMixin:
|
||||
}
|
||||
return Response(SyncStatusSerializer(status).data)
|
||||
|
||||
@extend_schema(
|
||||
request=SyncObjectSerializer,
|
||||
responses={200: SyncObjectResultSerializer()},
|
||||
)
|
||||
@action(
|
||||
methods=["POST"],
|
||||
detail=True,
|
||||
pagination_class=None,
|
||||
url_path="sync/object",
|
||||
filter_backends=[ObjectFilter],
|
||||
)
|
||||
def sync_object(self, request: Request, pk: int) -> Response:
|
||||
"""Sync/Re-sync a single user/group object"""
|
||||
provider: OutgoingSyncProvider = self.get_object()
|
||||
params = SyncObjectSerializer(data=request.data)
|
||||
params.is_valid(raise_exception=True)
|
||||
res: list[LogEvent] = self.sync_objects_task.delay(
|
||||
params.validated_data["sync_object_model"],
|
||||
page=1,
|
||||
provider_pk=provider.pk,
|
||||
pk=params.validated_data["sync_object_id"],
|
||||
).get()
|
||||
return Response(SyncObjectResultSerializer(instance={"messages": res}).data)
|
||||
|
||||
|
||||
class OutgoingSyncConnectionCreateMixin:
|
||||
"""Mixin for connection objects that fetches remote data upon creation"""
|
||||
|
@ -105,7 +105,7 @@ class SyncTasks:
|
||||
return
|
||||
task.set_status(TaskStatus.SUCCESSFUL, *messages)
|
||||
|
||||
def sync_objects(self, object_type: str, page: int, provider_pk: int, **filter):
|
||||
def sync_objects(self, object_type: str, page: int, provider_pk: int):
|
||||
_object_type = path_to_class(object_type)
|
||||
self.logger = get_logger().bind(
|
||||
provider_type=class_to_path(self._provider_model),
|
||||
@ -120,7 +120,7 @@ class SyncTasks:
|
||||
client = provider.client_for_model(_object_type)
|
||||
except TransientSyncException:
|
||||
return messages
|
||||
paginator = Paginator(provider.get_object_qs(_object_type).filter(**filter), PAGE_SIZE)
|
||||
paginator = Paginator(provider.get_object_qs(_object_type), PAGE_SIZE)
|
||||
if client.can_discover:
|
||||
self.logger.debug("starting discover")
|
||||
client.discover()
|
||||
|
@ -26,6 +26,7 @@ from authentik.outposts.apps import MANAGED_OUTPOST, MANAGED_OUTPOST_NAME
|
||||
from authentik.outposts.models import (
|
||||
Outpost,
|
||||
OutpostConfig,
|
||||
OutpostState,
|
||||
OutpostType,
|
||||
default_outpost_config,
|
||||
)
|
||||
@ -181,6 +182,7 @@ class OutpostViewSet(UsedByMixin, ModelViewSet):
|
||||
outpost: Outpost = self.get_object()
|
||||
states = []
|
||||
for state in outpost.state:
|
||||
state: OutpostState
|
||||
states.append(
|
||||
{
|
||||
"uid": state.uid,
|
||||
|
@ -26,7 +26,6 @@ from authentik.outposts.models import (
|
||||
KubernetesServiceConnection,
|
||||
OutpostServiceConnection,
|
||||
)
|
||||
from authentik.rbac.filters import ObjectFilter
|
||||
|
||||
|
||||
class ServiceConnectionSerializer(ModelSerializer, MetaNameSerializer):
|
||||
@ -76,7 +75,7 @@ class ServiceConnectionViewSet(
|
||||
filterset_fields = ["name"]
|
||||
|
||||
@extend_schema(responses={200: ServiceConnectionStateSerializer(many=False)})
|
||||
@action(detail=True, pagination_class=None, filter_backends=[ObjectFilter])
|
||||
@action(detail=True, pagination_class=None, filter_backends=[])
|
||||
def state(self, request: Request, pk: str) -> Response:
|
||||
"""Get the service connection's state"""
|
||||
connection = self.get_object()
|
||||
|
@ -451,7 +451,7 @@ class OutpostState:
|
||||
return False
|
||||
if self.build_hash != get_build_hash():
|
||||
return False
|
||||
return parse(self.version) != OUR_VERSION
|
||||
return parse(self.version) < OUR_VERSION
|
||||
|
||||
@staticmethod
|
||||
def for_outpost(outpost: Outpost) -> list["OutpostState"]:
|
||||
|
@ -214,7 +214,7 @@ def outpost_post_save(model_class: str, model_pk: Any):
|
||||
if not hasattr(instance, field_name):
|
||||
continue
|
||||
|
||||
LOGGER.debug("triggering outpost update from field", field=field.name)
|
||||
LOGGER.debug("triggering outpost update from from field", field=field.name)
|
||||
# Because the Outpost Model has an M2M to Provider,
|
||||
# we have to iterate over the entire QS
|
||||
for reverse in getattr(instance, field_name).all():
|
||||
|
@ -36,7 +36,7 @@ def update_score(request: HttpRequest, identifier: str, amount: int):
|
||||
if not created:
|
||||
reputation.score = F("score") + amount
|
||||
reputation.save()
|
||||
LOGGER.info("Updated score", amount=amount, for_user=identifier, for_ip=remote_ip)
|
||||
LOGGER.debug("Updated score", amount=amount, for_user=identifier, for_ip=remote_ip)
|
||||
|
||||
|
||||
@receiver(login_failed)
|
||||
|
@ -2,25 +2,15 @@
|
||||
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models.query import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django_filters.filters import BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import BooleanField, CharField, ListField, SerializerMethodField
|
||||
from rest_framework.fields import CharField, ListField, SerializerMethodField
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
|
||||
from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.core.api.utils import ModelSerializer, PassiveSerializer
|
||||
from authentik.core.models import Application
|
||||
from authentik.policies.api.exec import PolicyTestResultSerializer
|
||||
from authentik.policies.engine import PolicyEngine
|
||||
from authentik.policies.types import PolicyResult
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
from authentik.providers.ldap.models import LDAPProvider
|
||||
|
||||
|
||||
@ -33,6 +23,7 @@ class LDAPProviderSerializer(ProviderSerializer):
|
||||
model = LDAPProvider
|
||||
fields = ProviderSerializer.Meta.fields + [
|
||||
"base_dn",
|
||||
"search_group",
|
||||
"certificate",
|
||||
"tls_server_name",
|
||||
"uid_start_number",
|
||||
@ -64,6 +55,8 @@ class LDAPProviderFilter(FilterSet):
|
||||
"name": ["iexact"],
|
||||
"authorization_flow__slug": ["iexact"],
|
||||
"base_dn": ["iexact"],
|
||||
"search_group__group_uuid": ["iexact"],
|
||||
"search_group__name": ["iexact"],
|
||||
"certificate__kp_uuid": ["iexact"],
|
||||
"certificate__name": ["iexact"],
|
||||
"tls_server_name": ["iexact"],
|
||||
@ -102,6 +95,7 @@ class LDAPOutpostConfigSerializer(ModelSerializer):
|
||||
"base_dn",
|
||||
"bind_flow_slug",
|
||||
"application_slug",
|
||||
"search_group",
|
||||
"certificate",
|
||||
"tls_server_name",
|
||||
"uid_start_number",
|
||||
@ -122,33 +116,3 @@ class LDAPOutpostConfigViewSet(ListModelMixin, GenericViewSet):
|
||||
ordering = ["name"]
|
||||
search_fields = ["name"]
|
||||
filterset_fields = ["name"]
|
||||
|
||||
class LDAPCheckAccessSerializer(PassiveSerializer):
|
||||
has_search_permission = BooleanField(required=False)
|
||||
access = PolicyTestResultSerializer()
|
||||
|
||||
@extend_schema(
|
||||
request=None,
|
||||
parameters=[OpenApiParameter("app_slug", OpenApiTypes.STR)],
|
||||
responses={
|
||||
200: LDAPCheckAccessSerializer(),
|
||||
},
|
||||
operation_id="outposts_ldap_access_check",
|
||||
)
|
||||
@action(detail=True)
|
||||
def check_access(self, request: Request, pk) -> Response:
|
||||
"""Check access to a single application by slug"""
|
||||
provider = get_object_or_404(LDAPProvider, pk=pk)
|
||||
application = get_object_or_404(Application, slug=request.query_params["app_slug"])
|
||||
engine = PolicyEngine(application, request.user, request)
|
||||
engine.use_cache = False
|
||||
engine.build()
|
||||
result = engine.result
|
||||
access_response = PolicyResult(result.passing)
|
||||
response = self.LDAPCheckAccessSerializer(
|
||||
instance={
|
||||
"has_search_permission": request.user.has_perm("search_full_directory", provider),
|
||||
"access": access_response,
|
||||
}
|
||||
)
|
||||
return Response(response.data)
|
||||
|
@ -1,54 +0,0 @@
|
||||
# Generated by Django 5.0.7 on 2024-07-25 14:59
|
||||
from django.apps.registry import Apps
|
||||
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
from django.db import migrations
|
||||
from django.contrib.auth.management import create_permissions
|
||||
|
||||
|
||||
def migrate_search_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
from guardian.shortcuts import assign_perm
|
||||
from authentik.core.models import User
|
||||
from django.apps import apps as real_apps
|
||||
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
# Permissions are only created _after_ migrations are run
|
||||
# - https://github.com/django/django/blob/43cdfa8b20e567a801b7d0a09ec67ddd062d5ea4/django/contrib/auth/apps.py#L19
|
||||
# - https://stackoverflow.com/a/72029063/1870445
|
||||
create_permissions(real_apps.get_app_config("authentik_providers_ldap"), using=db_alias)
|
||||
|
||||
LDAPProvider = apps.get_model("authentik_providers_ldap", "ldapprovider")
|
||||
|
||||
for provider in LDAPProvider.objects.using(db_alias).all():
|
||||
for user_pk in (
|
||||
provider.search_group.users.using(db_alias).all().values_list("pk", flat=True)
|
||||
):
|
||||
# We need the correct user model instance to assign the permission
|
||||
assign_perm(
|
||||
"search_full_directory", User.objects.using(db_alias).get(pk=user_pk), provider
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_ldap", "0003_ldapprovider_mfa_support_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="ldapprovider",
|
||||
options={
|
||||
"permissions": [("search_full_directory", "Search full LDAP directory")],
|
||||
"verbose_name": "LDAP Provider",
|
||||
"verbose_name_plural": "LDAP Providers",
|
||||
},
|
||||
),
|
||||
migrations.RunPython(migrate_search_group),
|
||||
migrations.RemoveField(
|
||||
model_name="ldapprovider",
|
||||
name="search_group",
|
||||
),
|
||||
]
|
@ -7,7 +7,7 @@ from django.templatetags.static import static
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.serializers import Serializer
|
||||
|
||||
from authentik.core.models import BackchannelProvider
|
||||
from authentik.core.models import BackchannelProvider, Group
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.outposts.models import OutpostModel
|
||||
|
||||
@ -27,6 +27,17 @@ class LDAPProvider(OutpostModel, BackchannelProvider):
|
||||
help_text=_("DN under which objects are accessible."),
|
||||
)
|
||||
|
||||
search_group = models.ForeignKey(
|
||||
Group,
|
||||
null=True,
|
||||
default=None,
|
||||
on_delete=models.SET_DEFAULT,
|
||||
help_text=_(
|
||||
"Users in this group can do search queries. "
|
||||
"If not set, every user can execute search queries."
|
||||
),
|
||||
)
|
||||
|
||||
tls_server_name = models.TextField(
|
||||
default="",
|
||||
blank=True,
|
||||
@ -102,6 +113,3 @@ class LDAPProvider(OutpostModel, BackchannelProvider):
|
||||
class Meta:
|
||||
verbose_name = _("LDAP Provider")
|
||||
verbose_name_plural = _("LDAP Providers")
|
||||
permissions = [
|
||||
("search_full_directory", _("Search full LDAP directory")),
|
||||
]
|
||||
|
@ -105,7 +105,7 @@ class ScopeMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-scope-form"
|
||||
return "ak-property-mapping-scope-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -62,7 +62,7 @@ urlpatterns = [
|
||||
|
||||
api_urlpatterns = [
|
||||
("providers/oauth2", OAuth2ProviderViewSet),
|
||||
("propertymappings/provider/scope", ScopeMappingViewSet),
|
||||
("propertymappings/scope", ScopeMappingViewSet),
|
||||
("oauth2/authorization_codes", AuthorizationCodeViewSet),
|
||||
("oauth2/refresh_tokens", RefreshTokenViewSet),
|
||||
("oauth2/access_tokens", AccessTokenViewSet),
|
||||
|
@ -433,21 +433,20 @@ class TokenParams:
|
||||
app = Application.objects.filter(provider=self.provider).first()
|
||||
if not app or not app.provider:
|
||||
raise TokenError("invalid_grant")
|
||||
with audit_ignore():
|
||||
self.user, _ = User.objects.update_or_create(
|
||||
# trim username to ensure the entire username is max 150 chars
|
||||
# (22 chars being the length of the "template")
|
||||
username=f"ak-{self.provider.name[:150-22]}-client_credentials",
|
||||
defaults={
|
||||
"attributes": {
|
||||
USER_ATTRIBUTE_GENERATED: True,
|
||||
},
|
||||
"last_login": timezone.now(),
|
||||
"name": f"Autogenerated user from application {app.name} (client credentials)",
|
||||
"path": f"{USER_PATH_SYSTEM_PREFIX}/apps/{app.slug}",
|
||||
"type": UserTypes.SERVICE_ACCOUNT,
|
||||
self.user, _ = User.objects.update_or_create(
|
||||
# trim username to ensure the entire username is max 150 chars
|
||||
# (22 chars being the length of the "template")
|
||||
username=f"ak-{self.provider.name[:150-22]}-client_credentials",
|
||||
defaults={
|
||||
"attributes": {
|
||||
USER_ATTRIBUTE_GENERATED: True,
|
||||
},
|
||||
)
|
||||
"last_login": timezone.now(),
|
||||
"name": f"Autogenerated user from application {app.name} (client credentials)",
|
||||
"path": f"{USER_PATH_SYSTEM_PREFIX}/apps/{app.slug}",
|
||||
"type": UserTypes.SERVICE_ACCOUNT,
|
||||
},
|
||||
)
|
||||
self.__check_policy_access(app, request)
|
||||
|
||||
Event.new(
|
||||
|
@ -154,7 +154,6 @@ class RadiusOutpostConfigViewSet(ListModelMixin, GenericViewSet):
|
||||
responses={
|
||||
200: RadiusCheckAccessSerializer(),
|
||||
},
|
||||
operation_id="outposts_radius_access_check",
|
||||
)
|
||||
@action(detail=True)
|
||||
def check_access(self, request: Request, pk) -> Response:
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-12 12:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_radius", "0003_radiusproviderpropertymapping"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="radiusproviderpropertymapping",
|
||||
options={
|
||||
"verbose_name": "Radius Provider Property Mapping",
|
||||
"verbose_name_plural": "Radius Provider Property Mappings",
|
||||
},
|
||||
),
|
||||
]
|
@ -70,7 +70,7 @@ class RadiusProviderPropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-radius-form"
|
||||
return "ak-property-mapping-radius-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
@ -81,8 +81,8 @@ class RadiusProviderPropertyMapping(PropertyMapping):
|
||||
return RadiusProviderPropertyMappingSerializer
|
||||
|
||||
def __str__(self):
|
||||
return f"Radius Provider Property Mapping {self.name}"
|
||||
return f"Radius Property Mapping {self.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Radius Provider Property Mapping")
|
||||
verbose_name_plural = _("Radius Provider Property Mappings")
|
||||
verbose_name = _("Radius Property Mapping")
|
||||
verbose_name_plural = _("Radius Property Mappings")
|
||||
|
@ -7,7 +7,7 @@ from authentik.providers.radius.api.providers import (
|
||||
)
|
||||
|
||||
api_urlpatterns = [
|
||||
("propertymappings/provider/radius", RadiusProviderPropertyMappingViewSet),
|
||||
("propertymappings/radius", RadiusProviderPropertyMappingViewSet),
|
||||
("outposts/radius", RadiusOutpostConfigViewSet, "radiusprovideroutpost"),
|
||||
("providers/radius", RadiusProviderViewSet),
|
||||
]
|
||||
|
@ -133,17 +133,6 @@ class SAMLProviderSerializer(ProviderSerializer):
|
||||
except Provider.application.RelatedObjectDoesNotExist:
|
||||
return "-"
|
||||
|
||||
def validate(self, attrs: dict):
|
||||
if attrs.get("signing_kp"):
|
||||
if not attrs.get("sign_assertion") and not attrs.get("sign_response"):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"With a signing keypair selected, at least one of 'Sign assertion' "
|
||||
"and 'Sign Response' must be selected."
|
||||
)
|
||||
)
|
||||
return super().validate(attrs)
|
||||
|
||||
class Meta:
|
||||
model = SAMLProvider
|
||||
fields = ProviderSerializer.Meta.fields + [
|
||||
@ -159,9 +148,6 @@ class SAMLProviderSerializer(ProviderSerializer):
|
||||
"signature_algorithm",
|
||||
"signing_kp",
|
||||
"verification_kp",
|
||||
"encryption_kp",
|
||||
"sign_assertion",
|
||||
"sign_response",
|
||||
"sp_binding",
|
||||
"default_relay_state",
|
||||
"url_download_metadata",
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-12 12:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_saml", "0014_alter_samlprovider_digest_algorithm_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="samlpropertymapping",
|
||||
options={
|
||||
"verbose_name": "SAML Provider Property Mapping",
|
||||
"verbose_name_plural": "SAML Provider Property Mappings",
|
||||
},
|
||||
),
|
||||
]
|
@ -1,39 +0,0 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-15 14:52
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_crypto", "0004_alter_certificatekeypair_name"),
|
||||
("authentik_providers_saml", "0015_alter_samlpropertymapping_options"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="encryption_kp",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="When selected, incoming assertions are encrypted by the IdP using the public key of the encryption keypair. The assertion is decrypted by the SP using the the private key.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to="authentik_crypto.certificatekeypair",
|
||||
verbose_name="Encryption Keypair",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="sign_assertion",
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="samlprovider",
|
||||
name="sign_response",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
@ -144,28 +144,11 @@ class SAMLProvider(Provider):
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Signing Keypair"),
|
||||
)
|
||||
encryption_kp = models.ForeignKey(
|
||||
CertificateKeyPair,
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text=_(
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public "
|
||||
"key of the encryption keypair. The assertion is decrypted by the SP using the "
|
||||
"the private key."
|
||||
),
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Encryption Keypair"),
|
||||
related_name="+",
|
||||
)
|
||||
|
||||
default_relay_state = models.TextField(
|
||||
default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
|
||||
)
|
||||
|
||||
sign_assertion = models.BooleanField(default=True)
|
||||
sign_response = models.BooleanField(default=False)
|
||||
|
||||
@property
|
||||
def launch_url(self) -> str | None:
|
||||
"""Use IDP-Initiated SAML flow as launch URL"""
|
||||
@ -208,7 +191,7 @@ class SAMLPropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-saml-form"
|
||||
return "ak-property-mapping-saml-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
@ -221,8 +204,8 @@ class SAMLPropertyMapping(PropertyMapping):
|
||||
return f"{self.name} ({name})"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("SAML Provider Property Mapping")
|
||||
verbose_name_plural = _("SAML Provider Property Mappings")
|
||||
verbose_name = _("SAML Property Mapping")
|
||||
verbose_name_plural = _("SAML Property Mappings")
|
||||
|
||||
|
||||
class SAMLProviderImportModel(CreatableType, Provider):
|
||||
|
@ -18,11 +18,7 @@ from authentik.providers.saml.processors.authn_request_parser import AuthNReques
|
||||
from authentik.providers.saml.utils import get_random_id
|
||||
from authentik.providers.saml.utils.time import get_time_string
|
||||
from authentik.sources.ldap.auth import LDAP_DISTINGUISHED_NAME
|
||||
from authentik.sources.saml.exceptions import (
|
||||
InvalidEncryption,
|
||||
InvalidSignature,
|
||||
UnsupportedNameIDFormat,
|
||||
)
|
||||
from authentik.sources.saml.exceptions import InvalidSignature, UnsupportedNameIDFormat
|
||||
from authentik.sources.saml.processors.constants import (
|
||||
DIGEST_ALGORITHM_TRANSLATION_MAP,
|
||||
NS_MAP,
|
||||
@ -260,17 +256,9 @@ class AssertionProcessor:
|
||||
assertion,
|
||||
xmlsec.constants.TransformExclC14N,
|
||||
sign_algorithm_transform,
|
||||
ns=xmlsec.constants.DSigNs,
|
||||
ns="ds", # type: ignore
|
||||
)
|
||||
assertion.append(signature)
|
||||
if self.provider.encryption_kp:
|
||||
encryption = xmlsec.template.encrypted_data_create(
|
||||
assertion,
|
||||
xmlsec.constants.TransformAes128Cbc,
|
||||
self._assertion_id,
|
||||
ns=xmlsec.constants.DSigNs,
|
||||
)
|
||||
assertion.append(encryption)
|
||||
|
||||
assertion.append(self.get_assertion_subject())
|
||||
assertion.append(self.get_assertion_conditions())
|
||||
@ -298,86 +286,41 @@ class AssertionProcessor:
|
||||
response.append(self.get_assertion())
|
||||
return response
|
||||
|
||||
def _sign(self, element: Element):
|
||||
"""Sign an XML element based on the providers' configured signing settings"""
|
||||
digest_algorithm_transform = DIGEST_ALGORITHM_TRANSLATION_MAP.get(
|
||||
self.provider.digest_algorithm, xmlsec.constants.TransformSha1
|
||||
)
|
||||
xmlsec.tree.add_ids(element, ["ID"])
|
||||
signature_node = xmlsec.tree.find_node(element, xmlsec.constants.NodeSignature)
|
||||
ref = xmlsec.template.add_reference(
|
||||
signature_node,
|
||||
digest_algorithm_transform,
|
||||
uri="#" + self._assertion_id,
|
||||
)
|
||||
xmlsec.template.add_transform(ref, xmlsec.constants.TransformEnveloped)
|
||||
xmlsec.template.add_transform(ref, xmlsec.constants.TransformExclC14N)
|
||||
key_info = xmlsec.template.ensure_key_info(signature_node)
|
||||
xmlsec.template.add_x509_data(key_info)
|
||||
|
||||
ctx = xmlsec.SignatureContext()
|
||||
|
||||
key = xmlsec.Key.from_memory(
|
||||
self.provider.signing_kp.key_data,
|
||||
xmlsec.constants.KeyDataFormatPem,
|
||||
None,
|
||||
)
|
||||
key.load_cert_from_memory(
|
||||
self.provider.signing_kp.certificate_data,
|
||||
xmlsec.constants.KeyDataFormatCertPem,
|
||||
)
|
||||
ctx.key = key
|
||||
try:
|
||||
ctx.sign(signature_node)
|
||||
except xmlsec.Error as exc:
|
||||
raise InvalidSignature() from exc
|
||||
|
||||
def _encrypt(self, element: Element, parent: Element):
|
||||
"""Encrypt SAMLResponse EncryptedAssertion Element"""
|
||||
manager = xmlsec.KeysManager()
|
||||
key = xmlsec.Key.from_memory(
|
||||
self.provider.encryption_kp.key_data,
|
||||
xmlsec.constants.KeyDataFormatPem,
|
||||
)
|
||||
key.load_cert_from_memory(
|
||||
self.provider.encryption_kp.certificate_data,
|
||||
xmlsec.constants.KeyDataFormatCertPem,
|
||||
)
|
||||
|
||||
manager.add_key(key)
|
||||
encryption_context = xmlsec.EncryptionContext(manager)
|
||||
encryption_context.key = xmlsec.Key.generate(
|
||||
xmlsec.constants.KeyDataAes, 128, xmlsec.constants.KeyDataTypeSession
|
||||
)
|
||||
|
||||
container = SubElement(parent, f"{{{NS_SAML_ASSERTION}}}EncryptedAssertion")
|
||||
enc_data = xmlsec.template.encrypted_data_create(
|
||||
container, xmlsec.Transform.AES128, type=xmlsec.EncryptionType.ELEMENT, ns="xenc"
|
||||
)
|
||||
xmlsec.template.encrypted_data_ensure_cipher_value(enc_data)
|
||||
key_info = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="ds")
|
||||
enc_key = xmlsec.template.add_encrypted_key(key_info, xmlsec.Transform.RSA_OAEP)
|
||||
xmlsec.template.encrypted_data_ensure_cipher_value(enc_key)
|
||||
|
||||
try:
|
||||
enc_data = encryption_context.encrypt_xml(enc_data, element)
|
||||
except xmlsec.Error as exc:
|
||||
raise InvalidEncryption() from exc
|
||||
|
||||
parent.remove(enc_data)
|
||||
container.append(enc_data)
|
||||
|
||||
def build_response(self) -> str:
|
||||
"""Build string XML Response and sign if signing is enabled."""
|
||||
root_response = self.get_response()
|
||||
if self.provider.signing_kp:
|
||||
if self.provider.sign_assertion:
|
||||
assertion = root_response.xpath("//saml:Assertion", namespaces=NS_MAP)[0]
|
||||
self._sign(assertion)
|
||||
if self.provider.sign_response:
|
||||
response = root_response.xpath("//samlp:Response", namespaces=NS_MAP)[0]
|
||||
self._sign(response)
|
||||
if self.provider.encryption_kp:
|
||||
digest_algorithm_transform = DIGEST_ALGORITHM_TRANSLATION_MAP.get(
|
||||
self.provider.digest_algorithm, xmlsec.constants.TransformSha1
|
||||
)
|
||||
assertion = root_response.xpath("//saml:Assertion", namespaces=NS_MAP)[0]
|
||||
self._encrypt(assertion, root_response)
|
||||
xmlsec.tree.add_ids(assertion, ["ID"])
|
||||
signature_node = xmlsec.tree.find_node(assertion, xmlsec.constants.NodeSignature)
|
||||
ref = xmlsec.template.add_reference(
|
||||
signature_node,
|
||||
digest_algorithm_transform,
|
||||
uri="#" + self._assertion_id,
|
||||
)
|
||||
xmlsec.template.add_transform(ref, xmlsec.constants.TransformEnveloped)
|
||||
xmlsec.template.add_transform(ref, xmlsec.constants.TransformExclC14N)
|
||||
key_info = xmlsec.template.ensure_key_info(signature_node)
|
||||
xmlsec.template.add_x509_data(key_info)
|
||||
|
||||
ctx = xmlsec.SignatureContext()
|
||||
|
||||
key = xmlsec.Key.from_memory(
|
||||
self.provider.signing_kp.key_data,
|
||||
xmlsec.constants.KeyDataFormatPem,
|
||||
None,
|
||||
)
|
||||
key.load_cert_from_memory(
|
||||
self.provider.signing_kp.certificate_data,
|
||||
xmlsec.constants.KeyDataFormatCertPem,
|
||||
)
|
||||
ctx.key = key
|
||||
try:
|
||||
ctx.sign(signature_node)
|
||||
except xmlsec.Error as exc:
|
||||
raise InvalidSignature() from exc
|
||||
|
||||
return etree.tostring(root_response).decode("utf-8") # nosec
|
||||
|
@ -126,7 +126,7 @@ class MetadataProcessor:
|
||||
entity_descriptor,
|
||||
xmlsec.constants.TransformExclC14N,
|
||||
sign_algorithm_transform,
|
||||
ns=xmlsec.constants.DSigNs,
|
||||
ns="ds", # type: ignore
|
||||
)
|
||||
entity_descriptor.append(signature)
|
||||
|
||||
|
@ -8,7 +8,7 @@ from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.models import FlowDesignation
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.tests.utils import load_fixture
|
||||
@ -29,52 +29,12 @@ class TestSAMLProviderAPI(APITestCase):
|
||||
name=generate_id(),
|
||||
authorization_flow=create_test_flow(),
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:samlprovider-detail", kwargs={"pk": provider.pk}),
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
Application.objects.create(name=generate_id(), provider=provider, slug=generate_id())
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:samlprovider-detail", kwargs={"pk": provider.pk}),
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
def test_create_validate_signing_kp(self):
|
||||
"""Test create"""
|
||||
cert = create_test_cert()
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:samlprovider-list"),
|
||||
data={
|
||||
"name": generate_id(),
|
||||
"authorization_flow": create_test_flow().pk,
|
||||
"acs_url": "http://localhost",
|
||||
"signing_kp": cert.pk,
|
||||
},
|
||||
)
|
||||
self.assertEqual(400, response.status_code)
|
||||
self.assertJSONEqual(
|
||||
response.content,
|
||||
{
|
||||
"non_field_errors": [
|
||||
(
|
||||
"With a signing keypair selected, at least one "
|
||||
"of 'Sign assertion' and 'Sign Response' must be selected."
|
||||
)
|
||||
]
|
||||
},
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("authentik_api:samlprovider-list"),
|
||||
data={
|
||||
"name": generate_id(),
|
||||
"authorization_flow": create_test_flow().pk,
|
||||
"acs_url": "http://localhost",
|
||||
"signing_kp": cert.pk,
|
||||
"sign_assertion": True,
|
||||
},
|
||||
)
|
||||
self.assertEqual(201, response.status_code)
|
||||
|
||||
def test_metadata(self):
|
||||
"""Test metadata export (normal)"""
|
||||
self.client.logout()
|
||||
|
@ -78,12 +78,12 @@ class TestAuthNRequest(TestCase):
|
||||
|
||||
@apply_blueprint("system/providers-saml.yaml")
|
||||
def setUp(self):
|
||||
self.cert = create_test_cert()
|
||||
cert = create_test_cert()
|
||||
self.provider: SAMLProvider = SAMLProvider.objects.create(
|
||||
authorization_flow=create_test_flow(),
|
||||
acs_url="http://testserver/source/saml/provider/acs/",
|
||||
signing_kp=self.cert,
|
||||
verification_kp=self.cert,
|
||||
signing_kp=cert,
|
||||
verification_kp=cert,
|
||||
)
|
||||
self.provider.property_mappings.set(SAMLPropertyMapping.objects.all())
|
||||
self.provider.save()
|
||||
@ -91,8 +91,8 @@ class TestAuthNRequest(TestCase):
|
||||
slug="provider",
|
||||
issuer="authentik",
|
||||
pre_authentication_flow=create_test_flow(),
|
||||
signing_kp=self.cert,
|
||||
verification_kp=self.cert,
|
||||
signing_kp=cert,
|
||||
verification_kp=cert,
|
||||
)
|
||||
|
||||
def test_signed_valid(self):
|
||||
@ -112,34 +112,7 @@ class TestAuthNRequest(TestCase):
|
||||
self.assertEqual(parsed_request.id, request_proc.request_id)
|
||||
self.assertEqual(parsed_request.relay_state, "test_state")
|
||||
|
||||
def test_request_encrypt(self):
|
||||
"""Test full SAML Request/Response flow, fully encrypted"""
|
||||
self.provider.encryption_kp = self.cert
|
||||
self.provider.save()
|
||||
self.source.encryption_kp = self.cert
|
||||
self.source.save()
|
||||
http_request = get_request("/")
|
||||
|
||||
# First create an AuthNRequest
|
||||
request_proc = RequestProcessor(self.source, http_request, "test_state")
|
||||
request = request_proc.build_auth_n()
|
||||
|
||||
# To get an assertion we need a parsed request (parsed by provider)
|
||||
parsed_request = AuthNRequestParser(self.provider).parse(
|
||||
b64encode(request.encode()).decode(), "test_state"
|
||||
)
|
||||
# Now create a response and convert it to string (provider)
|
||||
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
|
||||
response = response_proc.build_response()
|
||||
|
||||
# Now parse the response (source)
|
||||
http_request.POST = QueryDict(mutable=True)
|
||||
http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode()
|
||||
|
||||
response_parser = ResponseProcessor(self.source, http_request)
|
||||
response_parser.parse()
|
||||
|
||||
def test_request_signed(self):
|
||||
def test_request_full_signed(self):
|
||||
"""Test full SAML Request/Response flow, fully signed"""
|
||||
http_request = get_request("/")
|
||||
|
||||
@ -162,32 +135,6 @@ class TestAuthNRequest(TestCase):
|
||||
response_parser = ResponseProcessor(self.source, http_request)
|
||||
response_parser.parse()
|
||||
|
||||
def test_request_signed_both(self):
|
||||
"""Test full SAML Request/Response flow, fully signed"""
|
||||
self.provider.sign_assertion = True
|
||||
self.provider.sign_response = True
|
||||
self.provider.save()
|
||||
http_request = get_request("/")
|
||||
|
||||
# First create an AuthNRequest
|
||||
request_proc = RequestProcessor(self.source, http_request, "test_state")
|
||||
request = request_proc.build_auth_n()
|
||||
|
||||
# To get an assertion we need a parsed request (parsed by provider)
|
||||
parsed_request = AuthNRequestParser(self.provider).parse(
|
||||
b64encode(request.encode()).decode(), "test_state"
|
||||
)
|
||||
# Now create a response and convert it to string (provider)
|
||||
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
|
||||
response = response_proc.build_response()
|
||||
|
||||
# Now parse the response (source)
|
||||
http_request.POST = QueryDict(mutable=True)
|
||||
http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode()
|
||||
|
||||
response_parser = ResponseProcessor(self.source, http_request)
|
||||
response_parser.parse()
|
||||
|
||||
def test_request_id_invalid(self):
|
||||
"""Test generated AuthNRequest with invalid request ID"""
|
||||
http_request = get_request("/")
|
||||
|
@ -54,11 +54,7 @@ class TestServiceProviderMetadataParser(TestCase):
|
||||
request = self.factory.get("/")
|
||||
metadata = lxml_from_string(MetadataProcessor(provider, request).build_entity_descriptor())
|
||||
|
||||
schema = etree.XMLSchema(
|
||||
etree.parse(
|
||||
source="schemas/saml-schema-metadata-2.0.xsd", parser=etree.XMLParser()
|
||||
) # nosec
|
||||
)
|
||||
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec
|
||||
self.assertTrue(schema.validate(metadata))
|
||||
|
||||
def test_schema_want_authn_requests_signed(self):
|
||||
|
@ -47,9 +47,7 @@ class TestSchema(TestCase):
|
||||
|
||||
metadata = lxml_from_string(request)
|
||||
|
||||
schema = etree.XMLSchema(
|
||||
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
|
||||
)
|
||||
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-protocol-2.0.xsd")) # nosec
|
||||
self.assertTrue(schema.validate(metadata))
|
||||
|
||||
def test_response_schema(self):
|
||||
@ -70,7 +68,5 @@ class TestSchema(TestCase):
|
||||
|
||||
metadata = lxml_from_string(response)
|
||||
|
||||
schema = etree.XMLSchema(
|
||||
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
|
||||
)
|
||||
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-protocol-2.0.xsd")) # nosec
|
||||
self.assertTrue(schema.validate(metadata))
|
||||
|
@ -44,6 +44,6 @@ urlpatterns = [
|
||||
]
|
||||
|
||||
api_urlpatterns = [
|
||||
("propertymappings/provider/saml", SAMLPropertyMappingViewSet),
|
||||
("propertymappings/saml", SAMLPropertyMappingViewSet),
|
||||
("providers/saml", SAMLProviderViewSet),
|
||||
]
|
||||
|
@ -6,7 +6,7 @@ from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.lib.sync.outgoing.api import OutgoingSyncProviderStatusMixin
|
||||
from authentik.providers.scim.models import SCIMProvider
|
||||
from authentik.providers.scim.tasks import scim_sync, scim_sync_objects
|
||||
from authentik.providers.scim.tasks import scim_sync
|
||||
|
||||
|
||||
class SCIMProviderSerializer(ProviderSerializer):
|
||||
@ -42,4 +42,3 @@ class SCIMProviderViewSet(OutgoingSyncProviderStatusMixin, UsedByMixin, ModelVie
|
||||
search_fields = ["name", "url"]
|
||||
ordering = ["name", "url"]
|
||||
sync_single_task = scim_sync
|
||||
sync_objects_task = scim_sync_objects
|
||||
|
@ -1,7 +1,5 @@
|
||||
"""Group client"""
|
||||
|
||||
from itertools import batched
|
||||
|
||||
from pydantic import ValidationError
|
||||
from pydanticscim.group import GroupMember
|
||||
from pydanticscim.responses import PatchOp, PatchOperation
|
||||
@ -58,22 +56,17 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
if not scim_group.externalId:
|
||||
scim_group.externalId = str(obj.pk)
|
||||
|
||||
if not self._config.patch.supported:
|
||||
users = list(obj.users.order_by("id").values_list("id", flat=True))
|
||||
connections = SCIMProviderUser.objects.filter(
|
||||
provider=self.provider, user__pk__in=users
|
||||
)
|
||||
members = []
|
||||
for user in connections:
|
||||
members.append(
|
||||
GroupMember(
|
||||
value=user.scim_id,
|
||||
)
|
||||
users = list(obj.users.order_by("id").values_list("id", flat=True))
|
||||
connections = SCIMProviderUser.objects.filter(provider=self.provider, user__pk__in=users)
|
||||
members = []
|
||||
for user in connections:
|
||||
members.append(
|
||||
GroupMember(
|
||||
value=user.scim_id,
|
||||
)
|
||||
if members:
|
||||
scim_group.members = members
|
||||
else:
|
||||
del scim_group.members
|
||||
)
|
||||
if members:
|
||||
scim_group.members = members
|
||||
return scim_group
|
||||
|
||||
def delete(self, obj: Group):
|
||||
@ -100,19 +93,16 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
scim_id = response.get("id")
|
||||
if not scim_id or scim_id == "":
|
||||
raise StopSync("SCIM Response with missing or invalid `id`")
|
||||
connection = SCIMProviderGroup.objects.create(
|
||||
return SCIMProviderGroup.objects.create(
|
||||
provider=self.provider, group=group, scim_id=scim_id
|
||||
)
|
||||
users = list(group.users.order_by("id").values_list("id", flat=True))
|
||||
self._patch_add_users(group, users)
|
||||
return connection
|
||||
|
||||
def update(self, group: Group, connection: SCIMProviderGroup):
|
||||
"""Update existing group"""
|
||||
scim_group = self.to_schema(group, connection)
|
||||
scim_group.id = connection.scim_id
|
||||
try:
|
||||
self._request(
|
||||
return self._request(
|
||||
"PUT",
|
||||
f"/Groups/{connection.scim_id}",
|
||||
json=scim_group.model_dump(
|
||||
@ -120,8 +110,6 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
exclude_unset=True,
|
||||
),
|
||||
)
|
||||
users = list(group.users.order_by("id").values_list("id", flat=True))
|
||||
return self._patch_add_users(group, users)
|
||||
except NotFoundSyncException:
|
||||
# Resource missing is handled by self.write, which will re-create the group
|
||||
raise
|
||||
@ -164,18 +152,14 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
group_id: str,
|
||||
*ops: PatchOperation,
|
||||
):
|
||||
chunk_size = self._config.bulk.maxOperations
|
||||
if chunk_size < 1:
|
||||
chunk_size = len(ops)
|
||||
for chunk in batched(ops, chunk_size):
|
||||
req = PatchRequest(Operations=list(chunk))
|
||||
self._request(
|
||||
"PATCH",
|
||||
f"/Groups/{group_id}",
|
||||
json=req.model_dump(
|
||||
mode="json",
|
||||
),
|
||||
)
|
||||
req = PatchRequest(Operations=ops)
|
||||
self._request(
|
||||
"PATCH",
|
||||
f"/Groups/{group_id}",
|
||||
json=req.model_dump(
|
||||
mode="json",
|
||||
),
|
||||
)
|
||||
|
||||
def _patch_add_users(self, group: Group, users_set: set[int]):
|
||||
"""Add users in users_set to group"""
|
||||
@ -196,14 +180,11 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
return
|
||||
self._patch(
|
||||
scim_group.scim_id,
|
||||
*[
|
||||
PatchOperation(
|
||||
op=PatchOp.add,
|
||||
path="members",
|
||||
value=[{"value": x}],
|
||||
)
|
||||
for x in user_ids
|
||||
],
|
||||
PatchOperation(
|
||||
op=PatchOp.add,
|
||||
path="members",
|
||||
value=[{"value": x} for x in user_ids],
|
||||
),
|
||||
)
|
||||
|
||||
def _patch_remove_users(self, group: Group, users_set: set[int]):
|
||||
@ -225,12 +206,9 @@ class SCIMGroupClient(SCIMClient[Group, SCIMProviderGroup, SCIMGroupSchema]):
|
||||
return
|
||||
self._patch(
|
||||
scim_group.scim_id,
|
||||
*[
|
||||
PatchOperation(
|
||||
op=PatchOp.remove,
|
||||
path="members",
|
||||
value=[{"value": x}],
|
||||
)
|
||||
for x in user_ids
|
||||
],
|
||||
PatchOperation(
|
||||
op=PatchOp.remove,
|
||||
path="members",
|
||||
value=[{"value": x} for x in user_ids],
|
||||
),
|
||||
)
|
||||
|
@ -1,11 +1,9 @@
|
||||
"""Custom SCIM schemas"""
|
||||
|
||||
from pydantic import Field
|
||||
from pydanticscim.group import Group as BaseGroup
|
||||
from pydanticscim.responses import PatchRequest as BasePatchRequest
|
||||
from pydanticscim.responses import SCIMError as BaseSCIMError
|
||||
from pydanticscim.service_provider import Bulk as BaseBulk
|
||||
from pydanticscim.service_provider import ChangePassword, Filter, Patch, Sort
|
||||
from pydanticscim.service_provider import Bulk, ChangePassword, Filter, Patch, Sort
|
||||
from pydanticscim.service_provider import (
|
||||
ServiceProviderConfiguration as BaseServiceProviderConfiguration,
|
||||
)
|
||||
@ -31,16 +29,10 @@ class Group(BaseGroup):
|
||||
meta: dict | None = None
|
||||
|
||||
|
||||
class Bulk(BaseBulk):
|
||||
|
||||
maxOperations: int = Field()
|
||||
|
||||
|
||||
class ServiceProviderConfiguration(BaseServiceProviderConfiguration):
|
||||
"""ServiceProviderConfig with fallback"""
|
||||
|
||||
_is_fallback: bool | None = False
|
||||
bulk: Bulk = Field(..., description="A complex type that specifies bulk configuration options.")
|
||||
|
||||
@property
|
||||
def is_fallback(self) -> bool:
|
||||
@ -53,7 +45,7 @@ class ServiceProviderConfiguration(BaseServiceProviderConfiguration):
|
||||
"""Get default configuration, which doesn't support any optional features as fallback"""
|
||||
return ServiceProviderConfiguration(
|
||||
patch=Patch(supported=False),
|
||||
bulk=Bulk(supported=False, maxOperations=0),
|
||||
bulk=Bulk(supported=False),
|
||||
filter=Filter(supported=False),
|
||||
changePassword=ChangePassword(supported=False),
|
||||
sort=Sort(supported=False),
|
||||
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.0.8 on 2024-08-12 12:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_scim", "0008_rename_scimgroup_scimprovidergroup_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="scimmapping",
|
||||
options={
|
||||
"verbose_name": "SCIM Provider Mapping",
|
||||
"verbose_name_plural": "SCIM Provider Mappings",
|
||||
},
|
||||
),
|
||||
]
|
@ -133,7 +133,7 @@ class SCIMMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-provider-scim-form"
|
||||
return "ak-property-mapping-scim-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
@ -142,8 +142,8 @@ class SCIMMapping(PropertyMapping):
|
||||
return SCIMMappingSerializer
|
||||
|
||||
def __str__(self):
|
||||
return f"SCIM Provider Mapping {self.name}"
|
||||
return f"SCIM Mapping {self.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("SCIM Provider Mapping")
|
||||
verbose_name_plural = _("SCIM Provider Mappings")
|
||||
verbose_name = _("SCIM Mapping")
|
||||
verbose_name_plural = _("SCIM Mappings")
|
||||
|
@ -13,5 +13,5 @@ api_urlpatterns = [
|
||||
("providers/scim", SCIMProviderViewSet),
|
||||
("providers/scim_users", SCIMProviderUserViewSet),
|
||||
("providers/scim_groups", SCIMProviderGroupViewSet),
|
||||
("propertymappings/provider/scim", SCIMMappingViewSet),
|
||||
("propertymappings/scim", SCIMMappingViewSet),
|
||||
]
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.management import _get_all_permissions
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db import models
|
||||
from django.db.transaction import atomic
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -10,26 +10,28 @@ from guardian.shortcuts import assign_perm
|
||||
from rest_framework.serializers import BaseSerializer
|
||||
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.lib.utils.reflection import get_apps
|
||||
|
||||
|
||||
def get_permission_choices():
|
||||
all_perms = []
|
||||
for app in get_apps():
|
||||
for model in app.get_models():
|
||||
for perm, _desc in _get_all_permissions(model._meta):
|
||||
all_perms.append((model, perm))
|
||||
return sorted(
|
||||
[
|
||||
(
|
||||
f"{model._meta.app_label}.{perm}",
|
||||
f"{model._meta.app_label}.{perm}",
|
||||
)
|
||||
for model, perm in all_perms
|
||||
]
|
||||
def get_permissions():
|
||||
return (
|
||||
Permission.objects.all()
|
||||
.select_related("content_type")
|
||||
.filter(
|
||||
content_type__app_label__startswith="authentik",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_permission_choices() -> list[tuple[str, str]]:
|
||||
return [
|
||||
(
|
||||
f"{x.content_type.app_label}.{x.codename}",
|
||||
f"{x.content_type.app_label}.{x.codename}",
|
||||
)
|
||||
for x in get_permissions()
|
||||
]
|
||||
|
||||
|
||||
class Role(SerializerModel):
|
||||
"""RBAC role, which can have different permissions (both global and per-object) attached
|
||||
to it."""
|
||||
|
@ -9,7 +9,6 @@ import orjson
|
||||
from celery.schedules import crontab
|
||||
from django.conf import ImproperlyConfigured
|
||||
from sentry_sdk import set_tag
|
||||
from xmlsec import enable_debug_trace
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.lib.config import CONFIG, redis_url
|
||||
@ -521,7 +520,6 @@ if DEBUG:
|
||||
"rest_framework.renderers.BrowsableAPIRenderer"
|
||||
)
|
||||
SHARED_APPS.insert(SHARED_APPS.index("django.contrib.staticfiles"), "daphne")
|
||||
enable_debug_trace(True)
|
||||
|
||||
TENANT_APPS.append("authentik.core")
|
||||
|
||||
|
@ -290,7 +290,7 @@ class LDAPSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-source-ldap-form"
|
||||
return "ak-property-mapping-ldap-source-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -268,7 +268,7 @@ class OAuthSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-source-oauth-form"
|
||||
return "ak-property-mapping-oauth-source-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -123,7 +123,7 @@ class PlexSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-source-plex-form"
|
||||
return "ak-property-mapping-plex-source-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -299,7 +299,7 @@ class SAMLSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-source-saml-form"
|
||||
return "ak-property-mapping-saml-source-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -6,14 +6,12 @@ NS_SAML_PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
||||
NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
||||
NS_SAML_METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
NS_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#"
|
||||
NS_ENC = "http://www.w3.org/2001/04/xmlenc#"
|
||||
|
||||
NS_MAP = {
|
||||
"samlp": NS_SAML_PROTOCOL,
|
||||
"saml": NS_SAML_ASSERTION,
|
||||
"ds": NS_SIGNATURE,
|
||||
"md": NS_SAML_METADATA,
|
||||
"xenc": NS_ENC,
|
||||
}
|
||||
|
||||
SAML_NAME_ID_FORMAT_EMAIL = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
||||
|
@ -76,7 +76,7 @@ class RequestProcessor:
|
||||
auth_n_request,
|
||||
xmlsec.constants.TransformExclC14N,
|
||||
sign_algorithm_transform,
|
||||
ns=xmlsec.constants.DSigNs,
|
||||
ns="ds", # type: ignore
|
||||
)
|
||||
auth_n_request.append(signature)
|
||||
|
||||
|
@ -30,9 +30,7 @@ class TestMetadataProcessor(TestCase):
|
||||
xml = MetadataProcessor(self.source, request).build_entity_descriptor()
|
||||
metadata = lxml_from_string(xml)
|
||||
|
||||
schema = etree.XMLSchema(
|
||||
etree.parse("schemas/saml-schema-metadata-2.0.xsd", parser=etree.XMLParser()) # nosec
|
||||
)
|
||||
schema = etree.XMLSchema(etree.parse("schemas/saml-schema-metadata-2.0.xsd")) # nosec
|
||||
self.assertTrue(schema.validate(metadata))
|
||||
|
||||
def test_metadata_consistent(self):
|
||||
|
@ -85,7 +85,7 @@ class SCIMSourcePropertyMapping(PropertyMapping):
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-property-mapping-source-scim-form"
|
||||
return "ak-property-mapping-scim-source-form"
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[Serializer]:
|
||||
|
@ -14,9 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name="duodevice",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.UTC)
|
||||
),
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
@ -14,9 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name="smsdevice",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.UTC)
|
||||
),
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
@ -14,9 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name="staticdevice",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.UTC)
|
||||
),
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
@ -14,9 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name="totpdevice",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.UTC)
|
||||
),
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
File diff suppressed because one or more lines are too long
@ -14,9 +14,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name="webauthndevice",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.UTC)
|
||||
),
|
||||
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(1, 1, 1, 0, 0)),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.3}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -52,7 +52,7 @@ services:
|
||||
- postgresql
|
||||
- redis
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.8.0}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2024.6.3}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
26
go.mod
26
go.mod
@ -1,8 +1,6 @@
|
||||
module goauthentik.io
|
||||
|
||||
go 1.23
|
||||
|
||||
toolchain go1.23.0
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
beryju.io/ldap v0.1.0
|
||||
@ -11,25 +9,26 @@ require (
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
github.com/go-openapi/runtime v0.28.0
|
||||
github.com/go-openapi/strfmt v0.23.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/gorilla/sessions v1.3.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jellydator/ttlcache/v3 v3.2.1
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/prometheus/client_golang v1.20.2
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/sethvargo/go-envconfig v1.1.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2024064.1
|
||||
goauthentik.io/api/v3 v3.2024063.6
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/sync v0.8.0
|
||||
@ -41,7 +40,7 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
@ -57,20 +56,17 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/loads v0.22.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
@ -79,6 +75,6 @@ require (
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
46
go.sum
46
go.sum
@ -48,8 +48,8 @@ github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdb
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@ -175,8 +175,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
@ -200,15 +200,13 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.1 h1:eS8ljnYY7BllYGkXw/TfczWZrXUu/CH7SIkC6ugn9Js=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.1/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
|
||||
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@ -217,14 +215,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaDr2CRKN++7HbSXbefUNDrPq60T+g24s=
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
@ -239,15 +233,15 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
|
||||
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -297,10 +291,10 @@ go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucg
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2024064.1 h1:vxquklgDGD+nGFhWRAsQ7ezQKg17MRq6bzEk25fbsb4=
|
||||
goauthentik.io/api/v3 v3.2024064.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||
goauthentik.io/api/v3 v3.2024063.6 h1:TloJKYEhdxej4PRPjQiA//SlaSByxc5XCYT3QmjErN8=
|
||||
goauthentik.io/api/v3 v3.2024063.6/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -578,8 +572,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2024.8.0"
|
||||
const VERSION = "2024.6.3"
|
||||
|
@ -77,17 +77,11 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
|
||||
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
||||
// The service account this token belongs to should only have access to a single outpost
|
||||
var outposts *api.PaginatedOutpostList
|
||||
var err error
|
||||
for {
|
||||
outposts, _, err = apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
||||
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
outposts, _, err := apiClient.OutpostsApi.OutpostsInstancesList(context.Background()).Execute()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to fetch outpost configuration, retrying in 3 seconds")
|
||||
time.Sleep(time.Second * 3)
|
||||
return NewAPIController(akURL, token)
|
||||
}
|
||||
if len(outposts.Results) < 1 {
|
||||
panic("No outposts found with given token, ensure the given token corresponds to an authenitk Outpost")
|
||||
@ -193,7 +187,7 @@ func (a *APIController) OnRefresh() error {
|
||||
func (a *APIController) getWebsocketPingArgs() map[string]interface{} {
|
||||
args := map[string]interface{}{
|
||||
"version": constants.VERSION,
|
||||
"buildHash": constants.BUILD(""),
|
||||
"buildHash": constants.BUILD("tagged"),
|
||||
"uuid": a.instanceUUID.String(),
|
||||
"golangVersion": runtime.Version(),
|
||||
"opensslEnabled": cryptobackend.OpensslEnabled,
|
||||
@ -213,7 +207,7 @@ func (a *APIController) StartBackgroundTasks() error {
|
||||
"outpost_type": a.Server.Type(),
|
||||
"uuid": a.instanceUUID.String(),
|
||||
"version": constants.VERSION,
|
||||
"build": constants.BUILD(""),
|
||||
"build": constants.BUILD("tagged"),
|
||||
}).Set(1)
|
||||
go func() {
|
||||
a.logger.Debug("Starting WS Handler...")
|
||||
|
@ -145,7 +145,7 @@ func (ac *APIController) startWSHandler() {
|
||||
"outpost_type": ac.Server.Type(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
"version": constants.VERSION,
|
||||
"build": constants.BUILD(""),
|
||||
"build": constants.BUILD("tagged"),
|
||||
}).SetToCurrentTime()
|
||||
}
|
||||
} else if wsMsg.Instruction == WebsocketInstructionProviderSpecific {
|
||||
@ -207,7 +207,7 @@ func (ac *APIController) startIntervalUpdater() {
|
||||
"outpost_type": ac.Server.Type(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
"version": constants.VERSION,
|
||||
"build": constants.BUILD(""),
|
||||
"build": constants.BUILD("tagged"),
|
||||
}).SetToCurrentTime()
|
||||
}
|
||||
ticker.Reset(getInterval())
|
||||
|
@ -120,6 +120,21 @@ func (fe *FlowExecutor) DelegateClientIP(a string) {
|
||||
fe.api.GetConfig().AddDefaultHeader(HeaderAuthentikRemoteIP, fe.cip)
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) {
|
||||
acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access")
|
||||
defer acsp.Finish()
|
||||
p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(acsp.Context(), appSlug).Execute()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check access: %w", err)
|
||||
}
|
||||
if !p.Passing {
|
||||
fe.log.Info("Access denied for user")
|
||||
return false, nil
|
||||
}
|
||||
fe.log.Debug("User has access")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) getAnswer(stage StageComponent) string {
|
||||
if v, o := fe.Answers[stage]; o {
|
||||
return v
|
||||
|
@ -58,10 +58,8 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
return ldap.LDAPResultInvalidCredentials, nil
|
||||
}
|
||||
|
||||
access, _, err := fe.ApiClient().OutpostsApi.OutpostsLdapAccessCheck(
|
||||
req.Context(), db.si.GetProviderID(),
|
||||
).AppSlug(db.si.GetAppSlug()).Execute()
|
||||
if !access.Access.Passing {
|
||||
access, err := fe.CheckApplicationAccess(db.si.GetAppSlug())
|
||||
if !access {
|
||||
req.Log().Info("Access denied for user")
|
||||
metrics.RequestsRejected.With(prometheus.Labels{
|
||||
"outpost_name": db.si.GetOutpostName(),
|
||||
@ -95,11 +93,12 @@ func (db *DirectBinder) Bind(username string, req *bind.Request) (ldap.LDAPResul
|
||||
req.Log().WithError(err).Warning("failed to get user info")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
cs := db.SearchAccessCheck(userInfo.User)
|
||||
flags.UserPk = userInfo.User.Pk
|
||||
flags.CanSearch = access.HasSearchPermission != nil
|
||||
flags.CanSearch = cs != nil
|
||||
db.si.SetFlags(req.BindDN, &flags)
|
||||
if flags.CanSearch {
|
||||
req.Log().Debug("Allowed access to search")
|
||||
req.Log().WithField("group", cs).Info("Allowed access to search")
|
||||
}
|
||||
uisp.Finish()
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api/v3"
|
||||
"goauthentik.io/internal/outpost/flow"
|
||||
"goauthentik.io/internal/outpost/ldap/server"
|
||||
"goauthentik.io/internal/outpost/ldap/utils"
|
||||
@ -46,6 +47,22 @@ func (db *DirectBinder) GetUsername(dn string) (string, error) {
|
||||
return "", errors.New("failed to find cn")
|
||||
}
|
||||
|
||||
// SearchAccessCheck Check if the current user is allowed to search
|
||||
func (db *DirectBinder) SearchAccessCheck(user api.UserSelf) *string {
|
||||
for _, group := range user.Groups {
|
||||
for _, allowedGroup := range db.si.GetSearchAllowedGroups() {
|
||||
if allowedGroup == nil {
|
||||
continue
|
||||
}
|
||||
db.log.WithField("userGroup", group.Pk).WithField("allowedGroup", allowedGroup).Trace("Checking search access")
|
||||
if group.Pk == allowedGroup.String() {
|
||||
return &group.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DirectBinder) TimerFlowCacheExpiry(ctx context.Context) {
|
||||
fe := flow.NewFlowExecutor(ctx, db.si.GetAuthenticationFlowSlug(), db.si.GetAPIClient().GetConfig(), log.Fields{})
|
||||
fe.Params.Add("goauthentik.io/outpost/ldap", "true")
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"goauthentik.io/api/v3"
|
||||
@ -30,13 +31,14 @@ type ProviderInstance struct {
|
||||
s *LDAPServer
|
||||
log *log.Entry
|
||||
|
||||
tlsServerName *string
|
||||
cert *tls.Certificate
|
||||
certUUID string
|
||||
outpostName string
|
||||
providerPk int32
|
||||
boundUsersMutex *sync.RWMutex
|
||||
boundUsers map[string]*flags.UserFlags
|
||||
tlsServerName *string
|
||||
cert *tls.Certificate
|
||||
certUUID string
|
||||
outpostName string
|
||||
outpostPk int32
|
||||
searchAllowedGroups []*strfmt.UUID
|
||||
boundUsersMutex *sync.RWMutex
|
||||
boundUsers map[string]*flags.UserFlags
|
||||
|
||||
uidStartNumber int32
|
||||
gidStartNumber int32
|
||||
@ -103,8 +105,8 @@ func (pi *ProviderInstance) GetInvalidationFlowSlug() string {
|
||||
return pi.invalidationFlowSlug
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetProviderID() int32 {
|
||||
return pi.providerPk
|
||||
func (pi *ProviderInstance) GetSearchAllowedGroups() []*strfmt.UUID {
|
||||
return pi.searchAllowedGroups
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) GetNeededObjects(scope int, baseDN string, filterOC string) (bool, bool) {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"goauthentik.io/api/v3"
|
||||
@ -22,7 +23,7 @@ import (
|
||||
|
||||
func (ls *LDAPServer) getCurrentProvider(pk int32) *ProviderInstance {
|
||||
for _, p := range ls.providers {
|
||||
if p.providerPk == pk {
|
||||
if p.outpostPk == pk {
|
||||
return p
|
||||
}
|
||||
}
|
||||
@ -76,6 +77,7 @@ func (ls *LDAPServer) Refresh() error {
|
||||
appSlug: provider.ApplicationSlug,
|
||||
authenticationFlowSlug: provider.BindFlowSlug,
|
||||
invalidationFlowSlug: invalidationFlow,
|
||||
searchAllowedGroups: []*strfmt.UUID{(*strfmt.UUID)(provider.SearchGroup.Get())},
|
||||
boundUsersMutex: usersMutex,
|
||||
boundUsers: users,
|
||||
s: ls,
|
||||
@ -85,7 +87,7 @@ func (ls *LDAPServer) Refresh() error {
|
||||
gidStartNumber: provider.GetGidStartNumber(),
|
||||
mfaSupport: provider.GetMfaSupport(),
|
||||
outpostName: ls.ac.Outpost.Name,
|
||||
providerPk: provider.Pk,
|
||||
outpostPk: provider.Pk,
|
||||
}
|
||||
if kp := provider.Certificate.Get(); kp != nil {
|
||||
err := ls.cs.AddKeypair(*kp)
|
||||
|
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"beryju.io/ldap"
|
||||
"github.com/go-openapi/strfmt"
|
||||
|
||||
"goauthentik.io/api/v3"
|
||||
"goauthentik.io/internal/outpost/ldap/flags"
|
||||
@ -14,7 +15,7 @@ type LDAPServerInstance interface {
|
||||
GetAuthenticationFlowSlug() string
|
||||
GetInvalidationFlowSlug() string
|
||||
GetAppSlug() string
|
||||
GetProviderID() int32
|
||||
GetSearchAllowedGroups() []*strfmt.UUID
|
||||
|
||||
UserEntry(u api.User) *ldap.Entry
|
||||
|
||||
|
@ -45,9 +45,7 @@ func (rs *RadiusServer) Handle_AccessRequest(w radius.ResponseWriter, r *RadiusR
|
||||
_ = w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
access, _, err := fe.ApiClient().OutpostsApi.OutpostsRadiusAccessCheck(
|
||||
r.Context(), r.pi.providerId,
|
||||
).AppSlug(r.pi.appSlug).Execute()
|
||||
access, _, err := fe.ApiClient().OutpostsApi.OutpostsRadiusCheckAccessRetrieve(r.Context(), r.pi.providerId).AppSlug(r.pi.appSlug).Execute()
|
||||
if err != nil {
|
||||
r.Log().WithField("username", username).WithError(err).Warning("failed to check access")
|
||||
_ = w.Write(r.Response(radius.CodeAccessReject))
|
||||
|
@ -1,7 +1,7 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build
|
||||
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.23-fips-bookworm AS builder
|
||||
FROM --platform=${BUILDPLATFORM} mcr.microsoft.com/oss/go/microsoft/golang:1.22-fips-bookworm AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
@ -36,11 +36,11 @@ FROM ghcr.io/goauthentik/fips-debian:bookworm-slim-fips
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.url=https://goauthentik.io
|
||||
LABEL org.opencontainers.image.description="goauthentik.io LDAP outpost, see https://goauthentik.io for more info."
|
||||
LABEL org.opencontainers.image.source=https://github.com/goauthentik/authentik
|
||||
LABEL org.opencontainers.image.version=${VERSION}
|
||||
LABEL org.opencontainers.image.revision=${GIT_BUILD_HASH}
|
||||
LABEL org.opencontainers.image.url https://goauthentik.io
|
||||
LABEL org.opencontainers.image.description goauthentik.io LDAP outpost, see https://goauthentik.io for more info.
|
||||
LABEL org.opencontainers.image.source https://github.com/goauthentik/authentik
|
||||
LABEL org.opencontainers.image.version ${VERSION}
|
||||
LABEL org.opencontainers.image.revision ${GIT_BUILD_HASH}
|
||||
|
||||
COPY --from=builder /go/ldap /
|
||||
|
||||
|
@ -10,20 +10,15 @@ from redis.exceptions import RedisError
|
||||
|
||||
from authentik.lib.config import CONFIG, redis_url
|
||||
|
||||
CHECK_THRESHOLD = 30
|
||||
|
||||
|
||||
def check_postgres():
|
||||
attempt = 0
|
||||
while True:
|
||||
if attempt >= CHECK_THRESHOLD:
|
||||
sysexit(1)
|
||||
try:
|
||||
conn = connect(
|
||||
dbname=CONFIG.refresh("postgresql.name"),
|
||||
user=CONFIG.refresh("postgresql.user"),
|
||||
password=CONFIG.refresh("postgresql.password"),
|
||||
host=CONFIG.refresh("postgresql.host"),
|
||||
dbname=CONFIG.get("postgresql.name"),
|
||||
user=CONFIG.get("postgresql.user"),
|
||||
password=CONFIG.get("postgresql.password"),
|
||||
host=CONFIG.get("postgresql.host"),
|
||||
port=CONFIG.get_int("postgresql.port"),
|
||||
sslmode=CONFIG.get("postgresql.sslmode"),
|
||||
sslrootcert=CONFIG.get("postgresql.sslrootcert"),
|
||||
@ -35,17 +30,12 @@ def check_postgres():
|
||||
except OperationalError as exc:
|
||||
sleep(1)
|
||||
CONFIG.log("info", f"PostgreSQL connection failed, retrying... ({exc})")
|
||||
finally:
|
||||
attempt += 1
|
||||
CONFIG.log("info", "PostgreSQL connection successful")
|
||||
|
||||
|
||||
def check_redis():
|
||||
url = CONFIG.get("cache.url") or redis_url(CONFIG.get("redis.db"))
|
||||
attempt = 0
|
||||
while True:
|
||||
if attempt >= CHECK_THRESHOLD:
|
||||
sysexit(1)
|
||||
try:
|
||||
redis = Redis.from_url(url)
|
||||
redis.ping()
|
||||
@ -53,8 +43,6 @@ def check_redis():
|
||||
except RedisError as exc:
|
||||
sleep(1)
|
||||
CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})")
|
||||
finally:
|
||||
attempt += 1
|
||||
CONFIG.log("info", "Redis Connection successful")
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-18 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-16 00:08+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"
|
||||
@ -75,12 +75,6 @@ msgid ""
|
||||
"and `ba.b`"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid ""
|
||||
"When set, external users will be redirected to this application after "
|
||||
"authenticating."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr ""
|
||||
@ -232,16 +226,6 @@ msgid ""
|
||||
"exists."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"Link to a group with identical name. Can have security implications when a "
|
||||
"group name is used with another source."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the group name, but deny enrollment when the name already exists."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Source's display Name."
|
||||
msgstr ""
|
||||
@ -264,12 +248,6 @@ msgid ""
|
||||
"new user enrolled."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"How the source determines if an existing group should be used or a new group "
|
||||
"created."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
@ -369,7 +347,6 @@ msgid "Go home"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/templates/login/base_full.html
|
||||
#: authentik/flows/templates/if/flow-sfe.html
|
||||
msgid "Powered by authentik"
|
||||
msgstr ""
|
||||
|
||||
@ -380,10 +357,6 @@ msgstr ""
|
||||
msgid "You're about to sign into %(application)s."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/core/views/interface.py
|
||||
msgid "Interface can only be accessed by internal users."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/crypto/api.py
|
||||
msgid "Subject-alt name"
|
||||
msgstr ""
|
||||
@ -460,7 +433,7 @@ msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr ""
|
||||
|
||||
@ -536,11 +509,11 @@ msgid "RAC Endpoints"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mapping"
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mappings"
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
@ -1036,30 +1009,6 @@ msgstr ""
|
||||
msgid "Expression Policies"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP not found in ASN database."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not part of an allowed autonomous system."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP address not found in City database."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not in an allowed country."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policy"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policies"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/models.py
|
||||
msgid "all, all policies must pass"
|
||||
msgstr ""
|
||||
@ -1212,6 +1161,12 @@ msgstr ""
|
||||
msgid "DN under which objects are accessible."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"Users in this group can do search queries. If not set, every user can "
|
||||
"execute search queries."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"The start for uidNumbers, this number is added to the user.pk to make sure "
|
||||
@ -1244,10 +1199,6 @@ msgstr ""
|
||||
msgid "LDAP Providers"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid "Search full LDAP directory"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/oauth2/id_token.py
|
||||
msgid "Based on the Hashed User ID"
|
||||
msgstr ""
|
||||
@ -1592,20 +1543,6 @@ msgstr ""
|
||||
msgid "Radius Providers"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid ""
|
||||
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
|
||||
"Response' must be selected."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid "Invalid XML Syntax"
|
||||
msgstr ""
|
||||
@ -1737,17 +1674,6 @@ msgstr ""
|
||||
msgid "Signing Keypair"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid ""
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public "
|
||||
"key of the encryption keypair. The assertion is decrypted by the SP using "
|
||||
"the the private key."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid "Encryption Keypair"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "Default relay_state value for IDP-initiated logins"
|
||||
msgstr ""
|
||||
@ -1761,11 +1687,11 @@ msgid "SAML Providers"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mapping"
|
||||
msgid "SAML Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mappings"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
@ -1793,11 +1719,11 @@ msgid "SCIM Providers"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mapping"
|
||||
msgid "SCIM Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mappings"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
@ -1926,11 +1852,11 @@ msgid "LDAP Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mapping"
|
||||
msgid "LDAP Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
@ -2098,14 +2024,6 @@ msgstr ""
|
||||
msgid "Reddit OAuth Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "User OAuth Source Connection"
|
||||
msgstr ""
|
||||
@ -2114,14 +2032,6 @@ msgstr ""
|
||||
msgid "User OAuth Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/oauth/views/callback.py
|
||||
#, python-brace-format
|
||||
msgid "Authentication failed: {reason}"
|
||||
@ -2153,14 +2063,6 @@ msgstr ""
|
||||
msgid "Plex Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "User Plex Source Connection"
|
||||
msgstr ""
|
||||
@ -2169,14 +2071,6 @@ msgstr ""
|
||||
msgid "User Plex Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Redirect Binding"
|
||||
msgstr ""
|
||||
@ -2251,14 +2145,6 @@ msgstr ""
|
||||
msgid "SAML Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr ""
|
||||
@ -2267,14 +2153,6 @@ msgstr ""
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connection"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr ""
|
||||
@ -2283,14 +2161,6 @@ msgstr ""
|
||||
msgid "SCIM Sources"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mappings"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/authenticator_duo/models.py
|
||||
msgid "Duo Authenticator Setup Stage"
|
||||
msgstr ""
|
||||
@ -2855,12 +2725,6 @@ msgid ""
|
||||
"out, use a reputation policy and a user_write stage."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid ""
|
||||
"When enabled, provides a 'show password' button with the password input "
|
||||
"field."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Password Stage"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-12 13:45+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2024\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -93,14 +93,6 @@ msgstr ""
|
||||
"Domain qui active cette marque. Peut être un super-ensemble, c'est-à-dire "
|
||||
"`a.b` pour `aa.b` et `ba.b`"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid ""
|
||||
"When set, external users will be redirected to this application after "
|
||||
"authenticating."
|
||||
msgstr ""
|
||||
"Si activé, les utilisateurs externes seront redirigés vers cette application"
|
||||
" après s'être authentifiés."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Certificate Web utilisé par le serveur web d'authentik core."
|
||||
@ -272,19 +264,6 @@ msgstr ""
|
||||
"Utiliser le nom d'utilisateur, mais refuser l'inscription si celui-ci existe"
|
||||
" déjà."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"Link to a group with identical name. Can have security implications when a "
|
||||
"group name is used with another source."
|
||||
msgstr ""
|
||||
"Lien vers un groupe ayant un nom identique. Peut poser des problèmes de "
|
||||
"sécurité si ce nom est partagé avec une autre source."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the group name, but deny enrollment when the name already exists."
|
||||
msgstr ""
|
||||
"Utiliser le nom du groupe, mais refuser la création si celui-ci existe déjà."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Source's display Name."
|
||||
msgstr "Nom d'affichage de la source."
|
||||
@ -309,14 +288,6 @@ msgstr ""
|
||||
"Comment la source détermine si un utilisateur existant doit être authentifié"
|
||||
" ou un nouvelle utilisateur doit être inscrit."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"How the source determines if an existing group should be used or a new group"
|
||||
" created."
|
||||
msgstr ""
|
||||
"Comment la source détermine si un groupe existant doit être utilisé ou un "
|
||||
"nouveau groupe doit être créé."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Token"
|
||||
msgstr "Jeton"
|
||||
@ -427,7 +398,6 @@ msgid "Go home"
|
||||
msgstr "Retourner à l'accueil"
|
||||
|
||||
#: authentik/core/templates/login/base_full.html
|
||||
#: authentik/flows/templates/if/flow-sfe.html
|
||||
msgid "Powered by authentik"
|
||||
msgstr "Propulsé par authentik"
|
||||
|
||||
@ -438,10 +408,6 @@ msgstr "Propulsé par authentik"
|
||||
msgid "You're about to sign into %(application)s."
|
||||
msgstr "Vous êtes sur le point de vous connecter à %(application)s."
|
||||
|
||||
#: authentik/core/views/interface.py
|
||||
msgid "Interface can only be accessed by internal users."
|
||||
msgstr "L'interface est accessible uniquement aux utilisateurs internes."
|
||||
|
||||
#: authentik/crypto/api.py
|
||||
msgid "Subject-alt name"
|
||||
msgstr "Nom alternatif subject"
|
||||
@ -502,25 +468,9 @@ msgstr "Entreprise est requis pour accéder à cette fonctionnalité."
|
||||
msgid "Feature only accessible for internal users."
|
||||
msgstr "Fonctionnalité accessible aux utilisateurs internes uniquement."
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Utilisateur du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Utilisateurs du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Groupe du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Groupes du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr ""
|
||||
"Mappages de propriétés utilisés lors de la création et de la mise à jour des"
|
||||
@ -542,17 +492,21 @@ msgstr "Mappage de propriété Google Workspace"
|
||||
msgid "Google Workspace Provider Mappings"
|
||||
msgstr "Mappages de propriété Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Utilisateur du fournisseur Microsoft Entra"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Utilisateur du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Groupe du fournisseur Microsoft Entra"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Utilisateurs du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Groupes du fournisseur Microsoft Entra"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Groupe du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Groupes du fournisseur Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider"
|
||||
@ -570,6 +524,18 @@ msgstr "Mappage de propriété Microsoft Entra"
|
||||
msgid "Microsoft Entra Provider Mappings"
|
||||
msgstr "Mappages de propriété Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Utilisateur du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Groupe du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Groupes du fournisseur Microsoft Entra"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
#: authentik/stages/user_login/models.py
|
||||
msgid ""
|
||||
@ -602,12 +568,12 @@ msgid "RAC Endpoints"
|
||||
msgstr "Points de terminaison RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mapping"
|
||||
msgstr "Mappage de propriété fournisseur RAC"
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr "Mappage de propriété RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mappings"
|
||||
msgstr "Mappages de propriété fournisseur RAC"
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr "Mappages de propriété RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Connection token"
|
||||
@ -1153,32 +1119,6 @@ msgstr "Politique d'Expression"
|
||||
msgid "Expression Policies"
|
||||
msgstr "Politiques d'expression"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP not found in ASN database."
|
||||
msgstr ""
|
||||
"GeoIP : l'IP du client n'a pas été trouvée dans la base de données ASN."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not part of an allowed autonomous system."
|
||||
msgstr "L'IP du client ne fait pas partie d'un autonomous system autorisé."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP address not found in City database."
|
||||
msgstr ""
|
||||
"GeoIP : l'IP du client n'a pas été trouvée dans la base de données Ville."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not in an allowed country."
|
||||
msgstr "L'IP du client ne fait pas partie d'un pays autorisé."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policy"
|
||||
msgstr "Politique GeoIP"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policies"
|
||||
msgstr "Politiques GeoIP"
|
||||
|
||||
#: authentik/policies/models.py
|
||||
msgid "all, all policies must pass"
|
||||
msgstr "toutes, toutes les politiques doivent réussir"
|
||||
@ -1788,14 +1728,6 @@ msgstr "Fournisseur Radius"
|
||||
msgid "Radius Providers"
|
||||
msgstr "Fournisseurs Radius"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mapping"
|
||||
msgstr "Mappage de propriété fournisseur Radius"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mappings"
|
||||
msgstr "Mappages de propriété fournisseur Radius"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid "Invalid XML Syntax"
|
||||
msgstr "Syntaxe XML Invalide"
|
||||
@ -1957,12 +1889,12 @@ msgid "SAML Providers"
|
||||
msgstr "Fournisseurs SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mapping"
|
||||
msgstr "Mappage de propriété fournisseur SAML"
|
||||
msgid "SAML Property Mapping"
|
||||
msgstr "Mappages de propriétés SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mappings"
|
||||
msgstr "Mappages de propriété fournisseur SAML"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr "Mappages de propriétés SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
@ -1989,12 +1921,12 @@ msgid "SCIM Providers"
|
||||
msgstr "Fournisseurs SCIM"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mapping"
|
||||
msgstr "Mappage fournisseur SCIM"
|
||||
msgid "SCIM Mapping"
|
||||
msgstr "Mappage SCIM"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mappings"
|
||||
msgstr "Mappages fournisseur SCIM"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "Mappages SCIM"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Role"
|
||||
@ -2129,12 +2061,12 @@ msgid "LDAP Sources"
|
||||
msgstr "Sources LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mapping"
|
||||
msgstr "Mappage de propriété source LDAP"
|
||||
msgid "LDAP Property Mapping"
|
||||
msgstr "Mappage de propriété LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Mappages de propriété source LDAP"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "Mappages de propriété LDAP"
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
@ -2305,14 +2237,6 @@ msgstr "Source d'OAuth Reddit"
|
||||
msgid "Reddit OAuth Sources"
|
||||
msgstr "Sources d'OAuth Reddit"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mapping"
|
||||
msgstr "Mappage de propriété source OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mappings"
|
||||
msgstr "Mappages de propriété source OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "User OAuth Source Connection"
|
||||
msgstr "Connexion de l'utilisateur à la source OAuth"
|
||||
@ -2321,14 +2245,6 @@ msgstr "Connexion de l'utilisateur à la source OAuth"
|
||||
msgid "User OAuth Source Connections"
|
||||
msgstr "Connexion de l'utilisateur aux sources OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connection"
|
||||
msgstr "Connexion du groupe à la source OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connections"
|
||||
msgstr "Connexions du groupe à la source OAuth"
|
||||
|
||||
#: authentik/sources/oauth/views/callback.py
|
||||
#, python-brace-format
|
||||
msgid "Authentication failed: {reason}"
|
||||
@ -2363,14 +2279,6 @@ msgstr "Source Plex"
|
||||
msgid "Plex Sources"
|
||||
msgstr "Sources Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mapping"
|
||||
msgstr "Mappage de propriété source Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mappings"
|
||||
msgstr "Mappages de propriété source Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "User Plex Source Connection"
|
||||
msgstr "Connexion de l'utilisateur à la source Plex"
|
||||
@ -2379,14 +2287,6 @@ msgstr "Connexion de l'utilisateur à la source Plex"
|
||||
msgid "User Plex Source Connections"
|
||||
msgstr "Connexion de l'utilisateur aux sources Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connection"
|
||||
msgstr "Connexion du groupe à la source Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connections"
|
||||
msgstr "Connexions du groupe à la source OAuth"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Redirect Binding"
|
||||
msgstr "Liaison de Redirection"
|
||||
@ -2466,20 +2366,6 @@ msgstr ""
|
||||
"Paire de clés utilisées pour signer les réponses sortantes allant vers le "
|
||||
"fournisseur d'identité."
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid ""
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public"
|
||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
|
||||
"the the private key."
|
||||
msgstr ""
|
||||
"Si activé, les assertions entrantes seront chiffrées par l'IdP avec la clé "
|
||||
"publique de la paire de clé de chiffrement. L'assertion est déchiffrée par "
|
||||
"le SP en utilisant la clé privée."
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Encryption Keypair"
|
||||
msgstr "Paire de clés de chiffrement"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source"
|
||||
msgstr "Source SAML"
|
||||
@ -2488,14 +2374,6 @@ msgstr "Source SAML"
|
||||
msgid "SAML Sources"
|
||||
msgstr "Sources SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mapping"
|
||||
msgstr "Mappage de propriété source SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mappings"
|
||||
msgstr "Mappages de propriété source SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "Connexion de l'utilisateur à la source SAML"
|
||||
@ -2504,14 +2382,6 @@ msgstr "Connexion de l'utilisateur à la source SAML"
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "Connexion de l'utilisateur aux sources SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connection"
|
||||
msgstr "Connexion du groupe à la source SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Connexions du groupe à la source SAML"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Source SCIM"
|
||||
@ -2520,14 +2390,6 @@ msgstr "Source SCIM"
|
||||
msgid "SCIM Sources"
|
||||
msgstr "Sources SCIM"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mapping"
|
||||
msgstr "Mappage de propriété source SCIM"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mappings"
|
||||
msgstr "Mappages de propriété source SCIM"
|
||||
|
||||
#: authentik/stages/authenticator_duo/models.py
|
||||
msgid "Duo Authenticator Setup Stage"
|
||||
msgstr "Étape de configuration du Duo Authenticator"
|
||||
@ -3155,14 +3017,6 @@ msgstr ""
|
||||
"annulé. Pour verrouiller l'utilisateur, utilisez une politique de réputation"
|
||||
" et une étape user_write."
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid ""
|
||||
"When enabled, provides a 'show password' button with the password input "
|
||||
"field."
|
||||
msgstr ""
|
||||
"Si activé, fourni un bouton « Montrer le mot de passe » avec le champ "
|
||||
"d'entrée mot de passe."
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Password Stage"
|
||||
msgstr "Étape de mot de passe"
|
||||
|
@ -9,16 +9,16 @@
|
||||
# Nicholas Winterhalter, 2023
|
||||
# Ренат Шарафутдинов, 2023
|
||||
# Stepan Karavaev, 2024
|
||||
# Anton Babenko, 2024
|
||||
# Anton, 2024
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-15 00:09+0000\n"
|
||||
"POT-Creation-Date: 2024-06-16 00:08+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Anton Babenko, 2024\n"
|
||||
"Last-Translator: Anton, 2024\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/authentik/teams/119923/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -89,14 +89,6 @@ msgstr ""
|
||||
"Домен, активирующий данный бренд. Может быть суперсетом, т.е. `a.b` для "
|
||||
"`aa.b` и `ba.b`."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid ""
|
||||
"When set, external users will be redirected to this application after "
|
||||
"authenticating."
|
||||
msgstr ""
|
||||
"Если этот параметр установлен, внешние пользователи будут перенаправляться в"
|
||||
" это приложение после аутентификации."
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "Web Certificate используемый для authentik Core webserver."
|
||||
@ -266,20 +258,6 @@ msgstr ""
|
||||
"Использовать имя пользователя, но отказывать в регистрации, если имя "
|
||||
"пользователя уже существует."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"Link to a group with identical name. Can have security implications when a "
|
||||
"group name is used with another source."
|
||||
msgstr ""
|
||||
"Связать с группой с идентичным именем. Может иметь последствия для "
|
||||
"безопасности, если имя группы используется в другом источнике."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the group name, but deny enrollment when the name already exists."
|
||||
msgstr ""
|
||||
"Использовать имя группы, но отказывать в регистрации, если имя уже "
|
||||
"существует."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Source's display Name."
|
||||
msgstr "Отображаемое имя источника."
|
||||
@ -304,14 +282,6 @@ msgstr ""
|
||||
"Как источник определяет, следует ли аутентифицировать существующего "
|
||||
"пользователя или зачислить нового."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"How the source determines if an existing group should be used or a new group"
|
||||
" created."
|
||||
msgstr ""
|
||||
"Как источник определяет, следует ли использовать существующую группу или "
|
||||
"создать новую."
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Token"
|
||||
msgstr "Токен"
|
||||
@ -423,7 +393,6 @@ msgid "Go home"
|
||||
msgstr "Домой"
|
||||
|
||||
#: authentik/core/templates/login/base_full.html
|
||||
#: authentik/flows/templates/if/flow-sfe.html
|
||||
msgid "Powered by authentik"
|
||||
msgstr "Основано на authentik"
|
||||
|
||||
@ -434,10 +403,6 @@ msgstr "Основано на authentik"
|
||||
msgid "You're about to sign into %(application)s."
|
||||
msgstr "Вы собираетесь войти в %(application)s."
|
||||
|
||||
#: authentik/core/views/interface.py
|
||||
msgid "Interface can only be accessed by internal users."
|
||||
msgstr "Доступ к интерфейсу могут иметь только внутренние пользователи."
|
||||
|
||||
#: authentik/crypto/api.py
|
||||
msgid "Subject-alt name"
|
||||
msgstr "Альтернативное имя субъекта"
|
||||
@ -516,7 +481,7 @@ msgstr "Группы провайдера Google Workspace"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr "Сопоставления свойств, используемые для создания/обновления групп."
|
||||
|
||||
@ -596,12 +561,12 @@ msgid "RAC Endpoints"
|
||||
msgstr "Точки подключения RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mapping"
|
||||
msgstr "Сопоставление свойства RAC провайдера"
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr "Сопоставление свойств RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mappings"
|
||||
msgstr "Сопоставление свойств RAC провайдера"
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr "Сопоставления свойств RAC"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Connection token"
|
||||
@ -1142,30 +1107,6 @@ msgstr "Политика выражения"
|
||||
msgid "Expression Policies"
|
||||
msgstr "Политики выражения"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP not found in ASN database."
|
||||
msgstr "GeoIP: IP-адрес клиента не найден в базе данных ASN."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not part of an allowed autonomous system."
|
||||
msgstr "IP-адрес клиента не входит в разрешенную автономную систему."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP address not found in City database."
|
||||
msgstr "GeoIP: IP-адрес клиента не найден в базе данных городов."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not in an allowed country."
|
||||
msgstr "IP-адрес клиента находится не в разрешенной стране."
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policy"
|
||||
msgstr "Политика GeoIP"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policies"
|
||||
msgstr "Политики GeoIP"
|
||||
|
||||
#: authentik/policies/models.py
|
||||
msgid "all, all policies must pass"
|
||||
msgstr "все, все политики должны пройти"
|
||||
@ -1326,6 +1267,14 @@ msgstr "Не удалось получить приложение"
|
||||
msgid "DN under which objects are accessible."
|
||||
msgstr "DN, под которым доступны объекты."
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"Users in this group can do search queries. If not set, every user can "
|
||||
"execute search queries."
|
||||
msgstr ""
|
||||
"Пользователи этой группы могут выполнять поисковые запросы. Если не задано, "
|
||||
"каждый пользователь может выполнять поисковые запросы."
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"The start for uidNumbers, this number is added to the user.pk to make sure "
|
||||
@ -1371,10 +1320,6 @@ msgstr "LDAP Провайдер"
|
||||
msgid "LDAP Providers"
|
||||
msgstr "LDAP Провайдеры"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid "Search full LDAP directory"
|
||||
msgstr "Поиск по всему каталогу LDAP"
|
||||
|
||||
#: authentik/providers/oauth2/id_token.py
|
||||
msgid "Based on the Hashed User ID"
|
||||
msgstr "На основе хэшированного идентификатора пользователя"
|
||||
@ -1763,14 +1708,6 @@ msgstr "Radius Провайдер"
|
||||
msgid "Radius Providers"
|
||||
msgstr "Radius Провайдеры"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mapping"
|
||||
msgstr "Сопоставление свойства Radius провайдера"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mappings"
|
||||
msgstr "Сопоставление свойств Radius провайдера"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid "Invalid XML Syntax"
|
||||
msgstr "Некорректный синтаксис XML"
|
||||
@ -1931,12 +1868,12 @@ msgid "SAML Providers"
|
||||
msgstr "SAML Провайдеры"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mapping"
|
||||
msgstr "Сопоставление свойства SAML провайдера"
|
||||
msgid "SAML Property Mapping"
|
||||
msgstr "Сопоставление свойств SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mappings"
|
||||
msgstr "Сопоставление свойств SAML провайдера"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr "Сопоставления свойств SAML"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
@ -1963,12 +1900,12 @@ msgid "SCIM Providers"
|
||||
msgstr "SCIM Провайдеры"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mapping"
|
||||
msgstr "Сопоставление свойства SCIM"
|
||||
msgid "SCIM Mapping"
|
||||
msgstr "Сопоставление SCIM"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mappings"
|
||||
msgstr "Сопоставления свойств SCIM"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "Сопоставления SCIM"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Role"
|
||||
@ -2105,12 +2042,12 @@ msgid "LDAP Sources"
|
||||
msgstr "Источники LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mapping"
|
||||
msgstr "Сопоставление свойства LDAP источника"
|
||||
msgid "LDAP Property Mapping"
|
||||
msgstr "Сопоставление свойств LDAP"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "Сопоставление свойств LDAP источника"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "Сопоставления свойств LDAP"
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
@ -2282,14 +2219,6 @@ msgstr "Источник Reddit OAuth"
|
||||
msgid "Reddit OAuth Sources"
|
||||
msgstr "Источники Reddit OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mapping"
|
||||
msgstr "Сопоставление свойства OAuth источника"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mappings"
|
||||
msgstr "Сопоставление свойств OAuth источника"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "User OAuth Source Connection"
|
||||
msgstr "Пользовательское подключение к источнику OAuth"
|
||||
@ -2298,14 +2227,6 @@ msgstr "Пользовательское подключение к источн
|
||||
msgid "User OAuth Source Connections"
|
||||
msgstr "Пользовательские подключения к источнику OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connection"
|
||||
msgstr "Групповое подключение к источнику OAuth"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connections"
|
||||
msgstr "Групповые подключения к источнику OAuth"
|
||||
|
||||
#: authentik/sources/oauth/views/callback.py
|
||||
#, python-brace-format
|
||||
msgid "Authentication failed: {reason}"
|
||||
@ -2340,14 +2261,6 @@ msgstr "Источник Plex"
|
||||
msgid "Plex Sources"
|
||||
msgstr "Источники Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mapping"
|
||||
msgstr "Сопоставление свойства Plex источника"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mappings"
|
||||
msgstr "Сопоставление свойств Plex источника"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "User Plex Source Connection"
|
||||
msgstr "Пользовательское подключение к источнику Plex"
|
||||
@ -2356,14 +2269,6 @@ msgstr "Пользовательское подключение к источн
|
||||
msgid "User Plex Source Connections"
|
||||
msgstr "Пользовательские подключения к источнику Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connection"
|
||||
msgstr "Групповое подключение к источнику Plex"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connections"
|
||||
msgstr "Групповые подключения к источнику Plex"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Redirect Binding"
|
||||
msgstr "Привязка переадресации"
|
||||
@ -2446,21 +2351,6 @@ msgstr ""
|
||||
"Пара ключей, используемая для подписи исходящих ответов, направляемых "
|
||||
"провайдеру идентификационных данных."
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid ""
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public"
|
||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
|
||||
"the the private key."
|
||||
msgstr ""
|
||||
"При выборе этого варианта, входящие утверждения шифруются поставщиком "
|
||||
"идентификации (IdP) с использованием открытого ключа из пары ключей "
|
||||
"шифрования. Утверждение расшифровывается поставщиком услуг (SP) с "
|
||||
"использованием закрытого ключа."
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Encryption Keypair"
|
||||
msgstr "Пара ключей шифрования"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source"
|
||||
msgstr "Источник SAML"
|
||||
@ -2469,14 +2359,6 @@ msgstr "Источник SAML"
|
||||
msgid "SAML Sources"
|
||||
msgstr "Источники SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mapping"
|
||||
msgstr "Сопоставление свойства SAML источника"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mappings"
|
||||
msgstr "Сопоставление свойств SAML источника"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "Пользовательское подключение к источнику SAML"
|
||||
@ -2485,14 +2367,6 @@ msgstr "Пользовательское подключение к источн
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "Пользовательские подключения к источнику SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connection"
|
||||
msgstr "Групповое подключение к источнику SAML"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "Групповые подключения к источнику SAML"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "Источник SCIM"
|
||||
@ -2501,14 +2375,6 @@ msgstr "Источник SCIM"
|
||||
msgid "SCIM Sources"
|
||||
msgstr "Источники SCIM"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mapping"
|
||||
msgstr "Сопоставление свойства SCIM источника"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mappings"
|
||||
msgstr "Сопоставление свойств SCIM источника"
|
||||
|
||||
#: authentik/stages/authenticator_duo/models.py
|
||||
msgid "Duo Authenticator Setup Stage"
|
||||
msgstr "Этап настройки аутентификатора Duo"
|
||||
@ -3142,14 +3008,6 @@ msgstr ""
|
||||
"Количество попыток пользователя до отмены потока. Чтобы заблокировать "
|
||||
"пользователя, используйте политику репутации и этап user_write."
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid ""
|
||||
"When enabled, provides a 'show password' button with the password input "
|
||||
"field."
|
||||
msgstr ""
|
||||
"Если эта функция включена, в поле ввода пароля отображается кнопка "
|
||||
"\"показать пароль\"."
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Password Stage"
|
||||
msgstr "Этап пароля"
|
||||
|
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-18 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -84,12 +84,6 @@ msgid ""
|
||||
"and `ba.b`"
|
||||
msgstr "激活此品牌的域。可以是超集,即 `a.b` 可以同时表示 `aa.b` 和 `ba.b`"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid ""
|
||||
"When set, external users will be redirected to this application after "
|
||||
"authenticating."
|
||||
msgstr "设置时,外部用户在验证身份后会被重定向到此应用程序。"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "authentik 核心 Web 服务器使用的 Web 证书。"
|
||||
@ -242,16 +236,6 @@ msgid ""
|
||||
"exists."
|
||||
msgstr "使用用户的用户名,但在用户名已存在时拒绝注册。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"Link to a group with identical name. Can have security implications when a "
|
||||
"group name is used with another source."
|
||||
msgstr "链接到名称相同的组。当其他源使用相同组名时,可能会有安全隐患。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the group name, but deny enrollment when the name already exists."
|
||||
msgstr "使用组的名称,但在名称已存在时拒绝注册。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Source's display Name."
|
||||
msgstr "源的显示名称。"
|
||||
@ -274,12 +258,6 @@ msgid ""
|
||||
"new user enrolled."
|
||||
msgstr "源怎样确定应该验证已有用户的身份还是注册新用户。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"How the source determines if an existing group should be used or a new group"
|
||||
" created."
|
||||
msgstr "源怎样确定应该使用已有组的身份还是创建新组。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Token"
|
||||
msgstr "令牌"
|
||||
@ -387,7 +365,6 @@ msgid "Go home"
|
||||
msgstr "前往首页"
|
||||
|
||||
#: authentik/core/templates/login/base_full.html
|
||||
#: authentik/flows/templates/if/flow-sfe.html
|
||||
msgid "Powered by authentik"
|
||||
msgstr "由 authentik 强力驱动"
|
||||
|
||||
@ -398,10 +375,6 @@ msgstr "由 authentik 强力驱动"
|
||||
msgid "You're about to sign into %(application)s."
|
||||
msgstr "您即将登录 %(application)s。"
|
||||
|
||||
#: authentik/core/views/interface.py
|
||||
msgid "Interface can only be accessed by internal users."
|
||||
msgstr "仅内部用户能访问此接口。"
|
||||
|
||||
#: authentik/crypto/api.py
|
||||
msgid "Subject-alt name"
|
||||
msgstr "替代名称"
|
||||
@ -460,25 +433,9 @@ msgstr "访问此功能需要企业版。"
|
||||
msgid "Feature only accessible for internal users."
|
||||
msgstr "仅内部用户能访问此功能。"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr "用于创建/更新组的属性映射。"
|
||||
|
||||
@ -498,17 +455,21 @@ msgstr "Google Workspace 提供程序映射"
|
||||
msgid "Google Workspace Provider Mappings"
|
||||
msgstr "Google Workspace 提供程序映射"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Microsoft Entra 提供程序用户"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider"
|
||||
@ -526,6 +487,18 @@ msgstr "Microsoft Entra 提供程序映射"
|
||||
msgid "Microsoft Entra Provider Mappings"
|
||||
msgstr "Microsoft Entra 提供程序映射"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Microsoft Entra 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
#: authentik/stages/user_login/models.py
|
||||
msgid ""
|
||||
@ -554,12 +527,12 @@ msgid "RAC Endpoints"
|
||||
msgstr "RAC 端点"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mapping"
|
||||
msgstr "RAC 提供程序属性映射"
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr "RAC 属性映射"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mappings"
|
||||
msgstr "RAC 提供程序属性映射"
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr "RAC 属性映射"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Connection token"
|
||||
@ -1058,30 +1031,6 @@ msgstr "表达式策略"
|
||||
msgid "Expression Policies"
|
||||
msgstr "表达式策略"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP not found in ASN database."
|
||||
msgstr "GeoIP:无法在 ASN 数据库中找到客户端 IP。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not part of an allowed autonomous system."
|
||||
msgstr "客户端 IP 不属于受允许的自治系统(AS)。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP address not found in City database."
|
||||
msgstr "GeoIP:无法在城市数据库中找到客户端 IP。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not in an allowed country."
|
||||
msgstr "客户端 IP 不在受允许的地区。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policy"
|
||||
msgstr "GeoIP 策略"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policies"
|
||||
msgstr "GeoIP 策略"
|
||||
|
||||
#: authentik/policies/models.py
|
||||
msgid "all, all policies must pass"
|
||||
msgstr "All,必须通过所有策略"
|
||||
@ -1237,6 +1186,12 @@ msgstr "解析应用程序失败"
|
||||
msgid "DN under which objects are accessible."
|
||||
msgstr "可访问对象的 DN。"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"Users in this group can do search queries. If not set, every user can "
|
||||
"execute search queries."
|
||||
msgstr "该组中的用户可以执行搜索查询。如果未设置,则每个用户都可以执行搜索查询。"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"The start for uidNumbers, this number is added to the user.pk to make sure "
|
||||
@ -1275,10 +1230,6 @@ msgstr "LDAP 提供程序"
|
||||
msgid "LDAP Providers"
|
||||
msgstr "LDAP 提供程序"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid "Search full LDAP directory"
|
||||
msgstr "搜索完整 LDAP 目录"
|
||||
|
||||
#: authentik/providers/oauth2/id_token.py
|
||||
msgid "Based on the Hashed User ID"
|
||||
msgstr "基于经过哈希处理的用户 ID"
|
||||
@ -1625,20 +1576,6 @@ msgstr "Radius 提供程序"
|
||||
msgid "Radius Providers"
|
||||
msgstr "Radius 提供程序"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mapping"
|
||||
msgstr "Radius 提供程序属性映射"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mappings"
|
||||
msgstr "Radius 提供程序属性映射"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid ""
|
||||
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
|
||||
"Response' must be selected."
|
||||
msgstr "选择签名密钥对后,必须至少选择“签名断言”和“签名响应”之一。"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid "Invalid XML Syntax"
|
||||
msgstr "无效 XML 语法"
|
||||
@ -1770,17 +1707,6 @@ msgstr "密钥对,用于签署发送给服务提供程序的传出响应。"
|
||||
msgid "Signing Keypair"
|
||||
msgstr "签名密钥对"
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid ""
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public"
|
||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
|
||||
"the the private key."
|
||||
msgstr "选择此选项后,传入的断言将由 IdP 使用加密密钥对的公钥进行加密。 SP 会使用私钥解密该断言。"
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid "Encryption Keypair"
|
||||
msgstr "加密密钥对"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "Default relay_state value for IDP-initiated logins"
|
||||
msgstr "用于 IDP 发起登录的默认 relay_state 值"
|
||||
@ -1794,12 +1720,12 @@ msgid "SAML Providers"
|
||||
msgstr "SAML 提供程序"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mapping"
|
||||
msgstr "SAML 提供程序属性映射"
|
||||
msgid "SAML Property Mapping"
|
||||
msgstr "SAML 属性映射"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mappings"
|
||||
msgstr "SAML 提供程序属性映射"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr "SAML 属性映射"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
@ -1826,12 +1752,12 @@ msgid "SCIM Providers"
|
||||
msgstr "SCIM 提供程序"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mapping"
|
||||
msgstr "SCIM 提供程序映射"
|
||||
msgid "SCIM Mapping"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mappings"
|
||||
msgstr "SCIM 提供程序映射"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Role"
|
||||
@ -1959,12 +1885,12 @@ msgid "LDAP Sources"
|
||||
msgstr "LDAP 源"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mapping"
|
||||
msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Property Mapping"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
@ -2132,14 +2058,6 @@ msgstr "Reddit OAuth 源"
|
||||
msgid "Reddit OAuth Sources"
|
||||
msgstr "Reddit OAuth 源"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mapping"
|
||||
msgstr "OAuth 源属性映射"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mappings"
|
||||
msgstr "OAuth 源属性映射"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "User OAuth Source Connection"
|
||||
msgstr "用户 OAuth 源连接"
|
||||
@ -2148,14 +2066,6 @@ msgstr "用户 OAuth 源连接"
|
||||
msgid "User OAuth Source Connections"
|
||||
msgstr "用户 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connection"
|
||||
msgstr "组 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connections"
|
||||
msgstr "组 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/views/callback.py
|
||||
#, python-brace-format
|
||||
msgid "Authentication failed: {reason}"
|
||||
@ -2187,14 +2097,6 @@ msgstr "Plex 源"
|
||||
msgid "Plex Sources"
|
||||
msgstr "Plex 源"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mapping"
|
||||
msgstr "Plex 源属性映射"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mappings"
|
||||
msgstr "Plex 源属性映射"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "User Plex Source Connection"
|
||||
msgstr "用户 Plex 源连接"
|
||||
@ -2203,14 +2105,6 @@ msgstr "用户 Plex 源连接"
|
||||
msgid "User Plex Source Connections"
|
||||
msgstr "用户 Plex 源连接"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connection"
|
||||
msgstr "组 Plex 源连接"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connections"
|
||||
msgstr "组 Plex 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Redirect Binding"
|
||||
msgstr "重定向绑定"
|
||||
@ -2289,14 +2183,6 @@ msgstr "SAML 源"
|
||||
msgid "SAML Sources"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mapping"
|
||||
msgstr "SAML 源属性映射"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mappings"
|
||||
msgstr "SAML 源属性映射"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "用户 SAML 源连接"
|
||||
@ -2305,14 +2191,6 @@ msgstr "用户 SAML 源连接"
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connection"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM 源"
|
||||
@ -2321,14 +2199,6 @@ msgstr "SCIM 源"
|
||||
msgid "SCIM Sources"
|
||||
msgstr "SCIM 源"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mapping"
|
||||
msgstr "SCIM 源属性映射"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mappings"
|
||||
msgstr "SCIM 源属性映射"
|
||||
|
||||
#: authentik/stages/authenticator_duo/models.py
|
||||
msgid "Duo Authenticator Setup Stage"
|
||||
msgstr "Duo 身份验证器设置阶段"
|
||||
@ -2911,12 +2781,6 @@ msgid ""
|
||||
"out, use a reputation policy and a user_write stage."
|
||||
msgstr "在取消流程之前,用户可以尝试多少次。要锁定用户,请使用信誉策略和 user_write 阶段。"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid ""
|
||||
"When enabled, provides a 'show password' button with the password input "
|
||||
"field."
|
||||
msgstr "启用时,在密码输入字段中提供“显示密码”按钮。"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Password Stage"
|
||||
msgstr "密码阶段"
|
||||
|
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-08-18 00:08+0000\n"
|
||||
"POT-Creation-Date: 2024-06-05 00:07+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2024\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -83,12 +83,6 @@ msgid ""
|
||||
"and `ba.b`"
|
||||
msgstr "激活此品牌的域。可以是超集,即 `a.b` 可以同时表示 `aa.b` 和 `ba.b`"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid ""
|
||||
"When set, external users will be redirected to this application after "
|
||||
"authenticating."
|
||||
msgstr "设置时,外部用户在验证身份后会被重定向到此应用程序。"
|
||||
|
||||
#: authentik/brands/models.py
|
||||
msgid "Web Certificate used by the authentik Core webserver."
|
||||
msgstr "authentik 核心 Web 服务器使用的 Web 证书。"
|
||||
@ -241,16 +235,6 @@ msgid ""
|
||||
"exists."
|
||||
msgstr "使用用户的用户名,但在用户名已存在时拒绝注册。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"Link to a group with identical name. Can have security implications when a "
|
||||
"group name is used with another source."
|
||||
msgstr "链接到名称相同的组。当其他源使用相同组名时,可能会有安全隐患。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Use the group name, but deny enrollment when the name already exists."
|
||||
msgstr "使用组的名称,但在名称已存在时拒绝注册。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Source's display Name."
|
||||
msgstr "源的显示名称。"
|
||||
@ -273,12 +257,6 @@ msgid ""
|
||||
"new user enrolled."
|
||||
msgstr "源怎样确定应该验证已有用户的身份还是注册新用户。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid ""
|
||||
"How the source determines if an existing group should be used or a new group"
|
||||
" created."
|
||||
msgstr "源怎样确定应该使用已有组的身份还是创建新组。"
|
||||
|
||||
#: authentik/core/models.py
|
||||
msgid "Token"
|
||||
msgstr "令牌"
|
||||
@ -386,7 +364,6 @@ msgid "Go home"
|
||||
msgstr "前往首页"
|
||||
|
||||
#: authentik/core/templates/login/base_full.html
|
||||
#: authentik/flows/templates/if/flow-sfe.html
|
||||
msgid "Powered by authentik"
|
||||
msgstr "由 authentik 强力驱动"
|
||||
|
||||
@ -397,10 +374,6 @@ msgstr "由 authentik 强力驱动"
|
||||
msgid "You're about to sign into %(application)s."
|
||||
msgstr "您即将登录 %(application)s。"
|
||||
|
||||
#: authentik/core/views/interface.py
|
||||
msgid "Interface can only be accessed by internal users."
|
||||
msgstr "仅内部用户能访问此接口。"
|
||||
|
||||
#: authentik/crypto/api.py
|
||||
msgid "Subject-alt name"
|
||||
msgstr "替代名称"
|
||||
@ -459,25 +432,9 @@ msgstr "访问此功能需要企业版。"
|
||||
msgid "Feature only accessible for internal users."
|
||||
msgstr "仅内部用户能访问此功能。"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
#: authentik/providers/scim/models.py
|
||||
#: authentik/providers/scim/models.py authentik/sources/ldap/models.py
|
||||
msgid "Property mappings used for group creation/updating."
|
||||
msgstr "用于创建/更新组的属性映射。"
|
||||
|
||||
@ -497,17 +454,21 @@ msgstr "Google Workspace 提供程序映射"
|
||||
msgid "Google Workspace Provider Mappings"
|
||||
msgstr "Google Workspace 提供程序映射"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Microsoft Entra 提供程序用户"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider User"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Users"
|
||||
msgstr "Google Workspace 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Group"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/google_workspace/models.py
|
||||
msgid "Google Workspace Provider Groups"
|
||||
msgstr "Google Workspace 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider"
|
||||
@ -525,6 +486,18 @@ msgstr "Microsoft Entra 提供程序映射"
|
||||
msgid "Microsoft Entra Provider Mappings"
|
||||
msgstr "Microsoft Entra 提供程序映射"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider User"
|
||||
msgstr "Microsoft Entra 提供程序用户"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Group"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/microsoft_entra/models.py
|
||||
msgid "Microsoft Entra Provider Groups"
|
||||
msgstr "Microsoft Entra 提供程序组"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
#: authentik/stages/user_login/models.py
|
||||
msgid ""
|
||||
@ -553,12 +526,12 @@ msgid "RAC Endpoints"
|
||||
msgstr "RAC 端点"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mapping"
|
||||
msgstr "RAC 提供程序属性映射"
|
||||
msgid "RAC Property Mapping"
|
||||
msgstr "RAC 属性映射"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Provider Property Mappings"
|
||||
msgstr "RAC 提供程序属性映射"
|
||||
msgid "RAC Property Mappings"
|
||||
msgstr "RAC 属性映射"
|
||||
|
||||
#: authentik/enterprise/providers/rac/models.py
|
||||
msgid "RAC Connection token"
|
||||
@ -1057,30 +1030,6 @@ msgstr "表达式策略"
|
||||
msgid "Expression Policies"
|
||||
msgstr "表达式策略"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP not found in ASN database."
|
||||
msgstr "GeoIP:无法在 ASN 数据库中找到客户端 IP。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not part of an allowed autonomous system."
|
||||
msgstr "客户端 IP 不属于受允许的自治系统(AS)。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP: client IP address not found in City database."
|
||||
msgstr "GeoIP:无法在城市数据库中找到客户端 IP。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "Client IP is not in an allowed country."
|
||||
msgstr "客户端 IP 不在受允许的地区。"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policy"
|
||||
msgstr "GeoIP 策略"
|
||||
|
||||
#: authentik/policies/geoip/models.py
|
||||
msgid "GeoIP Policies"
|
||||
msgstr "GeoIP 策略"
|
||||
|
||||
#: authentik/policies/models.py
|
||||
msgid "all, all policies must pass"
|
||||
msgstr "All,必须通过所有策略"
|
||||
@ -1236,6 +1185,12 @@ msgstr "解析应用程序失败"
|
||||
msgid "DN under which objects are accessible."
|
||||
msgstr "可访问对象的 DN。"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"Users in this group can do search queries. If not set, every user can "
|
||||
"execute search queries."
|
||||
msgstr "该组中的用户可以执行搜索查询。如果未设置,则每个用户都可以执行搜索查询。"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid ""
|
||||
"The start for uidNumbers, this number is added to the user.pk to make sure "
|
||||
@ -1274,10 +1229,6 @@ msgstr "LDAP 提供程序"
|
||||
msgid "LDAP Providers"
|
||||
msgstr "LDAP 提供程序"
|
||||
|
||||
#: authentik/providers/ldap/models.py
|
||||
msgid "Search full LDAP directory"
|
||||
msgstr "搜索完整 LDAP 目录"
|
||||
|
||||
#: authentik/providers/oauth2/id_token.py
|
||||
msgid "Based on the Hashed User ID"
|
||||
msgstr "基于经过哈希处理的用户 ID"
|
||||
@ -1624,20 +1575,6 @@ msgstr "Radius 提供程序"
|
||||
msgid "Radius Providers"
|
||||
msgstr "Radius 提供程序"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mapping"
|
||||
msgstr "Radius 提供程序属性映射"
|
||||
|
||||
#: authentik/providers/radius/models.py
|
||||
msgid "Radius Provider Property Mappings"
|
||||
msgstr "Radius 提供程序属性映射"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid ""
|
||||
"With a signing keypair selected, at least one of 'Sign assertion' and 'Sign "
|
||||
"Response' must be selected."
|
||||
msgstr "选择签名密钥对后,必须至少选择“签名断言”和“签名响应”之一。"
|
||||
|
||||
#: authentik/providers/saml/api/providers.py
|
||||
msgid "Invalid XML Syntax"
|
||||
msgstr "无效 XML 语法"
|
||||
@ -1769,17 +1706,6 @@ msgstr "密钥对,用于签署发送给服务提供程序的传出响应。"
|
||||
msgid "Signing Keypair"
|
||||
msgstr "签名密钥对"
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid ""
|
||||
"When selected, incoming assertions are encrypted by the IdP using the public"
|
||||
" key of the encryption keypair. The assertion is decrypted by the SP using "
|
||||
"the the private key."
|
||||
msgstr "选择此选项后,传入的断言将由 IdP 使用加密密钥对的公钥进行加密。 SP 会使用私钥解密该断言。"
|
||||
|
||||
#: authentik/providers/saml/models.py authentik/sources/saml/models.py
|
||||
msgid "Encryption Keypair"
|
||||
msgstr "加密密钥对"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "Default relay_state value for IDP-initiated logins"
|
||||
msgstr "用于 IDP 发起登录的默认 relay_state 值"
|
||||
@ -1793,12 +1719,12 @@ msgid "SAML Providers"
|
||||
msgstr "SAML 提供程序"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mapping"
|
||||
msgstr "SAML 提供程序属性映射"
|
||||
msgid "SAML Property Mapping"
|
||||
msgstr "SAML 属性映射"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider Property Mappings"
|
||||
msgstr "SAML 提供程序属性映射"
|
||||
msgid "SAML Property Mappings"
|
||||
msgstr "SAML 属性映射"
|
||||
|
||||
#: authentik/providers/saml/models.py
|
||||
msgid "SAML Provider from Metadata"
|
||||
@ -1825,12 +1751,12 @@ msgid "SCIM Providers"
|
||||
msgstr "SCIM 提供程序"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mapping"
|
||||
msgstr "SCIM 提供程序映射"
|
||||
msgid "SCIM Mapping"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/providers/scim/models.py
|
||||
msgid "SCIM Provider Mappings"
|
||||
msgstr "SCIM 提供程序映射"
|
||||
msgid "SCIM Mappings"
|
||||
msgstr "SCIM 映射"
|
||||
|
||||
#: authentik/rbac/models.py
|
||||
msgid "Role"
|
||||
@ -1958,12 +1884,12 @@ msgid "LDAP Sources"
|
||||
msgstr "LDAP 源"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mapping"
|
||||
msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Property Mapping"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/models.py
|
||||
msgid "LDAP Source Property Mappings"
|
||||
msgstr "LDAP 源属性映射"
|
||||
msgid "LDAP Property Mappings"
|
||||
msgstr "LDAP 属性映射"
|
||||
|
||||
#: authentik/sources/ldap/signals.py
|
||||
msgid "Password does not match Active Directory Complexity."
|
||||
@ -2131,14 +2057,6 @@ msgstr "Reddit OAuth 源"
|
||||
msgid "Reddit OAuth Sources"
|
||||
msgstr "Reddit OAuth 源"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mapping"
|
||||
msgstr "OAuth 源属性映射"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "OAuth Source Property Mappings"
|
||||
msgstr "OAuth 源属性映射"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "User OAuth Source Connection"
|
||||
msgstr "用户 OAuth 源连接"
|
||||
@ -2147,14 +2065,6 @@ msgstr "用户 OAuth 源连接"
|
||||
msgid "User OAuth Source Connections"
|
||||
msgstr "用户 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connection"
|
||||
msgstr "组 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/models.py
|
||||
msgid "Group OAuth Source Connections"
|
||||
msgstr "组 OAuth 源连接"
|
||||
|
||||
#: authentik/sources/oauth/views/callback.py
|
||||
#, python-brace-format
|
||||
msgid "Authentication failed: {reason}"
|
||||
@ -2186,14 +2096,6 @@ msgstr "Plex 源"
|
||||
msgid "Plex Sources"
|
||||
msgstr "Plex 源"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mapping"
|
||||
msgstr "Plex 源属性映射"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Plex Source Property Mappings"
|
||||
msgstr "Plex 源属性映射"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "User Plex Source Connection"
|
||||
msgstr "用户 Plex 源连接"
|
||||
@ -2202,14 +2104,6 @@ msgstr "用户 Plex 源连接"
|
||||
msgid "User Plex Source Connections"
|
||||
msgstr "用户 Plex 源连接"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connection"
|
||||
msgstr "组 Plex 源连接"
|
||||
|
||||
#: authentik/sources/plex/models.py
|
||||
msgid "Group Plex Source Connections"
|
||||
msgstr "组 Plex 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Redirect Binding"
|
||||
msgstr "重定向绑定"
|
||||
@ -2288,14 +2182,6 @@ msgstr "SAML 源"
|
||||
msgid "SAML Sources"
|
||||
msgstr "SAML 源"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mapping"
|
||||
msgstr "SAML 源属性映射"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "SAML Source Property Mappings"
|
||||
msgstr "SAML 源属性映射"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "User SAML Source Connection"
|
||||
msgstr "用户 SAML 源连接"
|
||||
@ -2304,14 +2190,6 @@ msgstr "用户 SAML 源连接"
|
||||
msgid "User SAML Source Connections"
|
||||
msgstr "用户 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connection"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/saml/models.py
|
||||
msgid "Group SAML Source Connections"
|
||||
msgstr "组 SAML 源连接"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source"
|
||||
msgstr "SCIM 源"
|
||||
@ -2320,14 +2198,6 @@ msgstr "SCIM 源"
|
||||
msgid "SCIM Sources"
|
||||
msgstr "SCIM 源"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mapping"
|
||||
msgstr "SCIM 源属性映射"
|
||||
|
||||
#: authentik/sources/scim/models.py
|
||||
msgid "SCIM Source Property Mappings"
|
||||
msgstr "SCIM 源属性映射"
|
||||
|
||||
#: authentik/stages/authenticator_duo/models.py
|
||||
msgid "Duo Authenticator Setup Stage"
|
||||
msgstr "Duo 身份验证器设置阶段"
|
||||
@ -2910,12 +2780,6 @@ msgid ""
|
||||
"out, use a reputation policy and a user_write stage."
|
||||
msgstr "在取消流程之前,用户可以尝试多少次。要锁定用户,请使用信誉策略和 user_write 阶段。"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid ""
|
||||
"When enabled, provides a 'show password' button with the password input "
|
||||
"field."
|
||||
msgstr "启用时,在密码输入字段中提供“显示密码”按钮。"
|
||||
|
||||
#: authentik/stages/password/models.py
|
||||
msgid "Password Stage"
|
||||
msgstr "密码阶段"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2024.8.0",
|
||||
"version": "2024.6.3",
|
||||
"private": true
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user