Merge branch 'main' into dev
* main: web/admin: fix SAML Provider preview (#9192) core, web: update translations (#9183) web: bump chromedriver from 123.0.1 to 123.0.2 in /tests/wdio (#9188) website: bump @types/react from 18.2.74 to 18.2.75 in /website (#9185) website/docs: update Postgresql username (#9190) core: bump maxmind/geoipupdate from v6.1 to v7.0 (#9186) events: add context manager to ignore/modify audit events being written (#9181) web: fix application library list display length and capability (#9094)
This commit is contained in:
@ -70,7 +70,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
|||||||
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server
|
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server
|
||||||
|
|
||||||
# Stage 4: MaxMind GeoIP
|
# Stage 4: MaxMind GeoIP
|
||||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v6.1 as geoip
|
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v7.0 as geoip
|
||||||
|
|
||||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City GeoLite2-ASN"
|
||||||
ENV GEOIPUPDATE_VERBOSE="true"
|
ENV GEOIPUPDATE_VERBOSE="true"
|
||||||
|
@ -22,6 +22,7 @@ from rest_framework.viewsets import ModelViewSet
|
|||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.admin.api.metrics import CoordinateSerializer
|
from authentik.admin.api.metrics import CoordinateSerializer
|
||||||
|
from authentik.api.pagination import Pagination
|
||||||
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
|
||||||
from authentik.core.api.providers import ProviderSerializer
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
@ -43,9 +44,12 @@ from authentik.rbac.filters import ObjectFilter
|
|||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
def user_app_cache_key(user_pk: str) -> str:
|
def user_app_cache_key(user_pk: str, page_number: int | None = None) -> str:
|
||||||
"""Cache key where application list for user is saved"""
|
"""Cache key where application list for user is saved"""
|
||||||
return f"{CACHE_PREFIX}/app_access/{user_pk}"
|
key = f"{CACHE_PREFIX}/app_access/{user_pk}"
|
||||||
|
if page_number:
|
||||||
|
key += f"/{page_number}"
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
class ApplicationSerializer(ModelSerializer):
|
class ApplicationSerializer(ModelSerializer):
|
||||||
@ -213,7 +217,8 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
|||||||
return super().list(request)
|
return super().list(request)
|
||||||
|
|
||||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||||
paginated_apps = self.paginate_queryset(queryset)
|
paginator: Pagination = self.paginator
|
||||||
|
paginated_apps = paginator.paginate_queryset(queryset, request)
|
||||||
|
|
||||||
if "for_user" in request.query_params:
|
if "for_user" in request.query_params:
|
||||||
try:
|
try:
|
||||||
@ -235,12 +240,14 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
|||||||
if not should_cache:
|
if not should_cache:
|
||||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
allowed_applications = self._get_allowed_applications(paginated_apps)
|
||||||
if should_cache:
|
if should_cache:
|
||||||
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
allowed_applications = cache.get(
|
||||||
|
user_app_cache_key(self.request.user.pk, paginator.page.number)
|
||||||
|
)
|
||||||
if not allowed_applications:
|
if not allowed_applications:
|
||||||
LOGGER.debug("Caching allowed application list")
|
LOGGER.debug("Caching allowed application list", page=paginator.page.number)
|
||||||
allowed_applications = self._get_allowed_applications(paginated_apps)
|
allowed_applications = self._get_allowed_applications(paginated_apps)
|
||||||
cache.set(
|
cache.set(
|
||||||
user_app_cache_key(self.request.user.pk),
|
user_app_cache_key(self.request.user.pk, paginator.page.number),
|
||||||
allowed_applications,
|
allowed_applications,
|
||||||
timeout=86400,
|
timeout=86400,
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Events middleware"""
|
"""Events middleware"""
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from contextvars import ContextVar
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -31,6 +33,9 @@ IGNORED_MODELS = tuple(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_CTX_OVERWRITE_USER = ContextVar[User | None]("authentik_events_log_overwrite_user", default=None)
|
||||||
|
_CTX_IGNORE = ContextVar[bool]("authentik_events_log_ignore", default=False)
|
||||||
|
|
||||||
|
|
||||||
def should_log_model(model: Model) -> bool:
|
def should_log_model(model: Model) -> bool:
|
||||||
"""Return true if operation on `model` should be logged"""
|
"""Return true if operation on `model` should be logged"""
|
||||||
@ -44,6 +49,28 @@ def should_log_m2m(model: Model) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def audit_overwrite_user(user: User):
|
||||||
|
"""Overwrite user being logged for model AuditMiddleware. Commonly used
|
||||||
|
for example in flows where a pending user is given, but the request is not authenticated yet"""
|
||||||
|
_CTX_OVERWRITE_USER.set(user)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
_CTX_OVERWRITE_USER.set(None)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def audit_ignore():
|
||||||
|
"""Ignore model operations in the block. Useful for objects which need to be modified
|
||||||
|
but are not excluded (e.g. WebAuthn devices)"""
|
||||||
|
_CTX_IGNORE.set(True)
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
_CTX_IGNORE.set(False)
|
||||||
|
|
||||||
|
|
||||||
class EventNewThread(Thread):
|
class EventNewThread(Thread):
|
||||||
"""Create Event in background thread"""
|
"""Create Event in background thread"""
|
||||||
|
|
||||||
@ -158,6 +185,10 @@ class AuditMiddleware:
|
|||||||
"""Signal handler for all object's post_save"""
|
"""Signal handler for all object's post_save"""
|
||||||
if not should_log_model(instance):
|
if not should_log_model(instance):
|
||||||
return
|
return
|
||||||
|
if _CTX_IGNORE.get():
|
||||||
|
return
|
||||||
|
if _new_user := _CTX_OVERWRITE_USER.get():
|
||||||
|
user = _new_user
|
||||||
|
|
||||||
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
||||||
thread = EventNewThread(action, request, user=user, model=model_to_dict(instance))
|
thread = EventNewThread(action, request, user=user, model=model_to_dict(instance))
|
||||||
@ -168,6 +199,10 @@ class AuditMiddleware:
|
|||||||
"""Signal handler for all object's pre_delete"""
|
"""Signal handler for all object's pre_delete"""
|
||||||
if not should_log_model(instance): # pragma: no cover
|
if not should_log_model(instance): # pragma: no cover
|
||||||
return
|
return
|
||||||
|
if _CTX_IGNORE.get():
|
||||||
|
return
|
||||||
|
if _new_user := _CTX_OVERWRITE_USER.get():
|
||||||
|
user = _new_user
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
EventAction.MODEL_DELETED,
|
EventAction.MODEL_DELETED,
|
||||||
@ -184,6 +219,10 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if not should_log_m2m(instance):
|
if not should_log_m2m(instance):
|
||||||
return
|
return
|
||||||
|
if _CTX_IGNORE.get():
|
||||||
|
return
|
||||||
|
if _new_user := _CTX_OVERWRITE_USER.get():
|
||||||
|
user = _new_user
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
EventAction.MODEL_UPDATED,
|
EventAction.MODEL_UPDATED,
|
||||||
|
@ -5,7 +5,9 @@ from rest_framework.test import APITestCase
|
|||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
|
from authentik.events.middleware import audit_ignore, audit_overwrite_user
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
|
from authentik.lib.generators import generate_id
|
||||||
|
|
||||||
|
|
||||||
class TestEventsMiddleware(APITestCase):
|
class TestEventsMiddleware(APITestCase):
|
||||||
@ -15,35 +17,74 @@ class TestEventsMiddleware(APITestCase):
|
|||||||
super().setUp()
|
super().setUp()
|
||||||
self.user = create_test_admin_user()
|
self.user = create_test_admin_user()
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
Event.objects.all().delete()
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
"""Test model creation event"""
|
"""Test model creation event"""
|
||||||
|
uid = generate_id()
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse("authentik_api:application-list"),
|
reverse("authentik_api:application-list"),
|
||||||
data={"name": "test-create", "slug": "test-create"},
|
data={"name": uid, "slug": uid},
|
||||||
)
|
)
|
||||||
self.assertTrue(Application.objects.filter(name="test-create").exists())
|
self.assertTrue(Application.objects.filter(name=uid).exists())
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
Event.objects.filter(
|
Event.objects.filter(
|
||||||
action=EventAction.MODEL_CREATED,
|
action=EventAction.MODEL_CREATED,
|
||||||
context__model__model_name="application",
|
context__model__model_name="application",
|
||||||
context__model__app="authentik_core",
|
context__model__app="authentik_core",
|
||||||
context__model__name="test-create",
|
context__model__name=uid,
|
||||||
).exists()
|
).exists()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
"""Test model creation event"""
|
"""Test model creation event"""
|
||||||
Application.objects.create(name="test-delete", slug="test-delete")
|
uid = generate_id()
|
||||||
self.client.delete(
|
Application.objects.create(name=uid, slug=uid)
|
||||||
reverse("authentik_api:application-detail", kwargs={"slug": "test-delete"})
|
self.client.delete(reverse("authentik_api:application-detail", kwargs={"slug": uid}))
|
||||||
)
|
|
||||||
self.assertFalse(Application.objects.filter(name="test").exists())
|
self.assertFalse(Application.objects.filter(name="test").exists())
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
Event.objects.filter(
|
Event.objects.filter(
|
||||||
action=EventAction.MODEL_DELETED,
|
action=EventAction.MODEL_DELETED,
|
||||||
context__model__model_name="application",
|
context__model__model_name="application",
|
||||||
context__model__app="authentik_core",
|
context__model__app="authentik_core",
|
||||||
context__model__name="test-delete",
|
context__model__name=uid,
|
||||||
|
).exists()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_audit_ignore(self):
|
||||||
|
"""Test audit_ignore context manager"""
|
||||||
|
uid = generate_id()
|
||||||
|
with audit_ignore():
|
||||||
|
self.client.post(
|
||||||
|
reverse("authentik_api:application-list"),
|
||||||
|
data={"name": uid, "slug": uid},
|
||||||
|
)
|
||||||
|
self.assertTrue(Application.objects.filter(name=uid).exists())
|
||||||
|
self.assertFalse(
|
||||||
|
Event.objects.filter(
|
||||||
|
action=EventAction.MODEL_CREATED,
|
||||||
|
context__model__model_name="application",
|
||||||
|
context__model__app="authentik_core",
|
||||||
|
context__model__name=uid,
|
||||||
|
).exists()
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_audit_overwrite_user(self):
|
||||||
|
"""Test audit_overwrite_user context manager"""
|
||||||
|
uid = generate_id()
|
||||||
|
new_user = create_test_admin_user()
|
||||||
|
with audit_overwrite_user(new_user):
|
||||||
|
self.client.post(
|
||||||
|
reverse("authentik_api:application-list"),
|
||||||
|
data={"name": uid, "slug": uid},
|
||||||
|
)
|
||||||
|
self.assertTrue(Application.objects.filter(name=uid).exists())
|
||||||
|
self.assertTrue(
|
||||||
|
Event.objects.filter(
|
||||||
|
action=EventAction.MODEL_CREATED,
|
||||||
|
context__model__model_name="application",
|
||||||
|
context__model__app="authentik_core",
|
||||||
|
context__model__name=uid,
|
||||||
|
user__username=new_user.username,
|
||||||
).exists()
|
).exists()
|
||||||
)
|
)
|
||||||
|
@ -31,6 +31,7 @@ from authentik.core.models import (
|
|||||||
User,
|
User,
|
||||||
UserTypes,
|
UserTypes,
|
||||||
)
|
)
|
||||||
|
from authentik.events.middleware import audit_ignore
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.events.signals import get_login_event
|
from authentik.events.signals import get_login_event
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION
|
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION
|
||||||
@ -465,22 +466,25 @@ class TokenParams:
|
|||||||
|
|
||||||
def __create_user_from_jwt(self, token: dict[str, Any], app: Application, source: OAuthSource):
|
def __create_user_from_jwt(self, token: dict[str, Any], app: Application, source: OAuthSource):
|
||||||
"""Create user from JWT"""
|
"""Create user from JWT"""
|
||||||
self.user, created = User.objects.update_or_create(
|
with audit_ignore():
|
||||||
username=f"{self.provider.name}-{token.get('sub')}",
|
self.user, created = User.objects.update_or_create(
|
||||||
defaults={
|
username=f"{self.provider.name}-{token.get('sub')}",
|
||||||
"attributes": {
|
defaults={
|
||||||
USER_ATTRIBUTE_GENERATED: True,
|
"attributes": {
|
||||||
|
USER_ATTRIBUTE_GENERATED: True,
|
||||||
|
},
|
||||||
|
"last_login": timezone.now(),
|
||||||
|
"name": (
|
||||||
|
f"Autogenerated user from application {app.name} (client credentials JWT)"
|
||||||
|
),
|
||||||
|
"path": source.get_user_path(),
|
||||||
|
"type": UserTypes.SERVICE_ACCOUNT,
|
||||||
},
|
},
|
||||||
"last_login": timezone.now(),
|
)
|
||||||
"name": f"Autogenerated user from application {app.name} (client credentials JWT)",
|
exp = token.get("exp")
|
||||||
"path": source.get_user_path(),
|
if created and exp:
|
||||||
"type": UserTypes.SERVICE_ACCOUNT,
|
self.user.attributes[USER_ATTRIBUTE_EXPIRES] = exp
|
||||||
},
|
self.user.save()
|
||||||
)
|
|
||||||
exp = token.get("exp")
|
|
||||||
if created and exp:
|
|
||||||
self.user.attributes[USER_ATTRIBUTE_EXPIRES] = exp
|
|
||||||
self.user.save()
|
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
|
@ -21,6 +21,7 @@ from webauthn.helpers.structs import UserVerificationRequirement
|
|||||||
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
from authentik.core.api.utils import JSONDictField, PassiveSerializer
|
||||||
from authentik.core.models import Application, User
|
from authentik.core.models import Application, User
|
||||||
from authentik.core.signals import login_failed
|
from authentik.core.signals import login_failed
|
||||||
|
from authentik.events.middleware import audit_ignore
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.stage import StageView
|
from authentik.flows.stage import StageView
|
||||||
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
|
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
|
||||||
@ -167,7 +168,8 @@ def validate_challenge_webauthn(data: dict, stage_view: StageView, user: User) -
|
|||||||
)
|
)
|
||||||
raise ValidationError("Assertion failed") from exc
|
raise ValidationError("Assertion failed") from exc
|
||||||
|
|
||||||
device.set_sign_count(authentication_verification.new_sign_count)
|
with audit_ignore():
|
||||||
|
device.set_sign_count(authentication_verification.new_sign_count)
|
||||||
return device
|
return device
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-04-01 23:02+0000\n"
|
"POT-Creation-Date: 2024-04-09 00:08+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -2251,6 +2251,19 @@ msgstr ""
|
|||||||
msgid "WebAuthn Devices"
|
msgid "WebAuthn Devices"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/authenticator_webauthn/models.py
|
||||||
|
msgid "WebAuthn Device type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/authenticator_webauthn/models.py
|
||||||
|
msgid "WebAuthn Device types"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: authentik/stages/authenticator_webauthn/stage.py
|
||||||
|
#, python-brace-format
|
||||||
|
msgid "Invalid device type. Contact your {brand} administrator for help."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: authentik/stages/captcha/models.py
|
#: authentik/stages/captcha/models.py
|
||||||
msgid "Public key, acquired your captcha Provider."
|
msgid "Public key, acquired your captcha Provider."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
8
tests/wdio/package-lock.json
generated
8
tests/wdio/package-lock.json
generated
@ -6,7 +6,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "@goauthentik/web-tests",
|
"name": "@goauthentik/web-tests",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chromedriver": "^123.0.1"
|
"chromedriver": "^123.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
@ -2084,9 +2084,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chromedriver": {
|
"node_modules/chromedriver": {
|
||||||
"version": "123.0.1",
|
"version": "123.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.2.tgz",
|
||||||
"integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==",
|
"integrity": "sha512-Kx0r/IGULm7eciaUtX/OKaFbdBdHRDSguiV1Q4zuQncz11gvymDdMtELa7ppk+kTL5113NLPud92nuIMNTRhww==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@testim/chrome-version": "^1.1.4",
|
"@testim/chrome-version": "^1.1.4",
|
||||||
|
@ -32,6 +32,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chromedriver": "^123.0.1"
|
"chromedriver": "^123.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -575,7 +575,11 @@ export class SAMLProviderViewPage extends AKElement {
|
|||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">
|
<div class="pf-c-description-list__text">
|
||||||
<ul class="pf-c-list"></ul>
|
<ul class="pf-c-list">
|
||||||
|
${attr.Value.map((value) => {
|
||||||
|
return html` <li><pre>${value}</pre></li> `;
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
@ -2,7 +2,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
|||||||
import { me } from "@goauthentik/common/users";
|
import { me } from "@goauthentik/common/users";
|
||||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/EmptyState";
|
import "@goauthentik/elements/EmptyState";
|
||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
|
||||||
|
|
||||||
import { localized, msg } from "@lit/localize";
|
import { localized, msg } from "@lit/localize";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
@ -25,6 +24,8 @@ import type { PageUIConfig } from "./types";
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const coreApi = () => new CoreApi(DEFAULT_CONFIG);
|
||||||
|
|
||||||
@localized()
|
@localized()
|
||||||
@customElement("ak-library")
|
@customElement("ak-library")
|
||||||
export class LibraryPage extends AKElement {
|
export class LibraryPage extends AKElement {
|
||||||
@ -35,15 +36,13 @@ export class LibraryPage extends AKElement {
|
|||||||
isAdmin = false;
|
isAdmin = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
apps!: PaginatedResponse<Application>;
|
apps: Application[] = [];
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
uiConfig: PageUIConfig;
|
uiConfig: PageUIConfig;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
const applicationListFetch = new CoreApi(DEFAULT_CONFIG).coreApplicationsList({});
|
|
||||||
const meFetch = me();
|
|
||||||
const uiConfig = rootInterface()?.uiConfig;
|
const uiConfig = rootInterface()?.uiConfig;
|
||||||
if (!uiConfig) {
|
if (!uiConfig) {
|
||||||
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
|
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
|
||||||
@ -55,22 +54,41 @@ export class LibraryPage extends AKElement {
|
|||||||
searchEnabled: uiConfig.enabledFeatures.search,
|
searchEnabled: uiConfig.enabledFeatures.search,
|
||||||
};
|
};
|
||||||
|
|
||||||
Promise.allSettled([applicationListFetch, meFetch]).then(
|
Promise.all([this.fetchApplications(), me()]).then(([applications, meStatus]) => {
|
||||||
([applicationListStatus, meStatus]) => {
|
this.isAdmin = meStatus.user.isSuperuser;
|
||||||
if (meStatus.status === "rejected") {
|
this.apps = applications;
|
||||||
throw new Error(
|
this.ready = true;
|
||||||
`Could not determine status of user. Reason: ${meStatus.reason}`,
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
|
async fetchApplications(): Promise<Application[]> {
|
||||||
|
const applicationListParams = (page = 1) => ({
|
||||||
|
ordering: "name",
|
||||||
|
page,
|
||||||
|
pageSize: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
const applicationListFetch = await coreApi().coreApplicationsList(applicationListParams(1));
|
||||||
|
const pageCount = applicationListFetch.pagination.totalPages;
|
||||||
|
if (pageCount === 1) {
|
||||||
|
return applicationListFetch.results;
|
||||||
|
}
|
||||||
|
|
||||||
|
const applicationLaterPages = await Promise.allSettled(
|
||||||
|
Array.from({ length: pageCount - 1 }).map((_a, idx) =>
|
||||||
|
coreApi().coreApplicationsList(applicationListParams(idx + 2)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return applicationLaterPages.reduce(
|
||||||
|
function (acc, result) {
|
||||||
|
if (result.status === "rejected") {
|
||||||
|
const reason = JSON.stringify(result.reason, null, 2);
|
||||||
|
throw new Error(`Could not retrieve list of applications. Reason: ${reason}`);
|
||||||
}
|
}
|
||||||
if (applicationListStatus.status === "rejected") {
|
return [...acc, ...result.value.results];
|
||||||
throw new Error(
|
|
||||||
`Could not retrieve list of applications. Reason: ${applicationListStatus.reason}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.isAdmin = meStatus.value.user.isSuperuser;
|
|
||||||
this.apps = applicationListStatus.value;
|
|
||||||
this.ready = true;
|
|
||||||
},
|
},
|
||||||
|
[...applicationListFetch.results],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { groupBy } from "@goauthentik/common/utils";
|
import { groupBy } from "@goauthentik/common/utils";
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import "@goauthentik/elements/EmptyState";
|
import "@goauthentik/elements/EmptyState";
|
||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
|
||||||
import "@goauthentik/user/LibraryApplication";
|
import "@goauthentik/user/LibraryApplication";
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
@ -42,8 +41,8 @@ export class LibraryPage extends AKElement {
|
|||||||
@property({ attribute: "isadmin", type: Boolean })
|
@property({ attribute: "isadmin", type: Boolean })
|
||||||
isAdmin = false;
|
isAdmin = false;
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false, type: Array })
|
||||||
apps!: PaginatedResponse<Application>;
|
apps!: Application[];
|
||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
uiConfig!: PageUIConfig;
|
uiConfig!: PageUIConfig;
|
||||||
@ -66,7 +65,7 @@ export class LibraryPage extends AKElement {
|
|||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.filteredApps = this.apps?.results;
|
this.filteredApps = this.apps;
|
||||||
if (this.filteredApps === undefined) {
|
if (this.filteredApps === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Application.results should never be undefined when passed to the Library Page.",
|
"Application.results should never be undefined when passed to the Library Page.",
|
||||||
@ -89,7 +88,7 @@ export class LibraryPage extends AKElement {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const apps = event.detail.apps;
|
const apps = event.detail.apps;
|
||||||
this.selectedApp = undefined;
|
this.selectedApp = undefined;
|
||||||
this.filteredApps = this.apps.results;
|
this.filteredApps = this.apps;
|
||||||
if (apps.length > 0) {
|
if (apps.length > 0) {
|
||||||
this.selectedApp = apps[0];
|
this.selectedApp = apps[0];
|
||||||
this.filteredApps = event.detail.apps;
|
this.filteredApps = event.detail.apps;
|
||||||
@ -132,7 +131,7 @@ export class LibraryPage extends AKElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSearch() {
|
renderSearch() {
|
||||||
return html`<ak-library-list-search .apps=${this.apps.results}></ak-library-list-search>`;
|
return html`<ak-library-list-search .apps=${this.apps}></ak-library-list-search>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -6475,6 +6475,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -6744,6 +6744,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -6391,6 +6391,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -8504,6 +8504,18 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -8332,6 +8332,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -8175,6 +8175,18 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -6596,6 +6596,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -8448,4 +8448,16 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
|
</trans-unit>
|
||||||
</body></file></xliff>
|
</body></file></xliff>
|
||||||
|
@ -6384,6 +6384,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -5302,6 +5302,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="s4caed5b7a7e5d89b">
|
<trans-unit id="s4caed5b7a7e5d89b">
|
||||||
@ -596,9 +596,9 @@
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="saa0e2675da69651b">
|
<trans-unit id="saa0e2675da69651b">
|
||||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||||
<target>未找到 URL "
|
<target>未找到 URL "
|
||||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s58cd9c2fe836d9c6">
|
<trans-unit id="s58cd9c2fe836d9c6">
|
||||||
@ -1040,8 +1040,8 @@
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa8384c9c26731f83">
|
<trans-unit id="sa8384c9c26731f83">
|
||||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s55787f4dfcdce52b">
|
<trans-unit id="s55787f4dfcdce52b">
|
||||||
@ -1782,8 +1782,8 @@
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa90b7809586c35ce">
|
<trans-unit id="sa90b7809586c35ce">
|
||||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s0410779cb47de312">
|
<trans-unit id="s0410779cb47de312">
|
||||||
@ -2961,8 +2961,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s76768bebabb7d543">
|
<trans-unit id="s76768bebabb7d543">
|
||||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s026555347e589f0e">
|
<trans-unit id="s026555347e589f0e">
|
||||||
@ -3739,8 +3739,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s7b1fba26d245cb1c">
|
<trans-unit id="s7b1fba26d245cb1c">
|
||||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s44536d20bb5c8257">
|
<trans-unit id="s44536d20bb5c8257">
|
||||||
@ -3916,10 +3916,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sa95a538bfbb86111">
|
<trans-unit id="sa95a538bfbb86111">
|
||||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||||
<target>您确定要更新
|
<target>您确定要更新
|
||||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||||
@ -5000,7 +5000,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sdf1d8edef27236f0">
|
<trans-unit id="sdf1d8edef27236f0">
|
||||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -5335,10 +5335,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s2d5f69929bb7221d">
|
<trans-unit id="s2d5f69929bb7221d">
|
||||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||||
<target>
|
<target>
|
||||||
<x id="0" equiv-text="${prompt.name}"/>("
|
<x id="0" equiv-text="${prompt.name}"/>("
|
||||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||||
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -5387,7 +5387,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s1608b2f94fa0dbd4">
|
<trans-unit id="s1608b2f94fa0dbd4">
|
||||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||||
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -7839,7 +7839,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
<target>成功创建用户并添加到组 <x id="0" equiv-text="${this.group.name}"/></target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s824e0943a7104668">
|
<trans-unit id="s824e0943a7104668">
|
||||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||||
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
<target>此用户将会被添加到组 &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;。</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||||
@ -8523,7 +8523,19 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
<target>需要进行选择</target>
|
<target>需要进行选择</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
@ -6432,6 +6432,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -8293,6 +8293,18 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sc7d071fb5cc1f6bf">
|
<trans-unit id="sc7d071fb5cc1f6bf">
|
||||||
<source>A selection is required</source>
|
<source>A selection is required</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sa64fb483becc9c2c">
|
||||||
|
<source>Device type restrictions</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="sbb928551c84cd63f">
|
||||||
|
<source>Available Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s6446c35d6b411e53">
|
||||||
|
<source>Selected Device types</source>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="s1f4df216b56de4ac">
|
||||||
|
<source>Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed.</source>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
|
@ -27,7 +27,7 @@ cd /bitnami/postgresql/
|
|||||||
# Set the postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable
|
# Set the postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable
|
||||||
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
||||||
# Dump the authentik database into an sql file
|
# Dump the authentik database into an sql file
|
||||||
pg_dump -U postgres $POSTGRES_DB > dump-11.sql
|
pg_dump -U $POSTGRES_USER $POSTGRES_DB > dump-11.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
### Stop PostgreSQL and start the upgrade
|
### Stop PostgreSQL and start the upgrade
|
||||||
@ -88,7 +88,7 @@ Run the following commands to restore the data:
|
|||||||
cd /bitnami/postgresql/
|
cd /bitnami/postgresql/
|
||||||
# Set the Postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable.
|
# Set the Postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable.
|
||||||
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
|
||||||
psql -U postgres $POSTGRES_DB < dump-11.sql
|
psql -U $POSTGRES_USER $POSTGRES_DB < dump-11.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
After the last command finishes, all of the data is restored, and you can restart authentik.
|
After the last command finishes, all of the data is restored, and you can restart authentik.
|
||||||
|
8
website/package-lock.json
generated
8
website/package-lock.json
generated
@ -33,7 +33,7 @@
|
|||||||
"@docusaurus/module-type-aliases": "3.2.1",
|
"@docusaurus/module-type-aliases": "3.2.1",
|
||||||
"@docusaurus/tsconfig": "3.2.1",
|
"@docusaurus/tsconfig": "3.2.1",
|
||||||
"@docusaurus/types": "3.2.1",
|
"@docusaurus/types": "3.2.1",
|
||||||
"@types/react": "^18.2.74",
|
"@types/react": "^18.2.75",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"typescript": "~5.4.4"
|
"typescript": "~5.4.4"
|
||||||
},
|
},
|
||||||
@ -3902,9 +3902,9 @@
|
|||||||
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA=="
|
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "18.2.74",
|
"version": "18.2.75",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.75.tgz",
|
||||||
"integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==",
|
"integrity": "sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
"@docusaurus/module-type-aliases": "3.2.1",
|
"@docusaurus/module-type-aliases": "3.2.1",
|
||||||
"@docusaurus/tsconfig": "3.2.1",
|
"@docusaurus/tsconfig": "3.2.1",
|
||||||
"@docusaurus/types": "3.2.1",
|
"@docusaurus/types": "3.2.1",
|
||||||
"@types/react": "^18.2.74",
|
"@types/react": "^18.2.75",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"typescript": "~5.4.4"
|
"typescript": "~5.4.4"
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user