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:
Ken Sternberg
2024-04-09 08:47:08 -07:00
28 changed files with 374 additions and 91 deletions

View File

@ -70,7 +70,7 @@ RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server
# 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_VERBOSE="true"

View File

@ -22,6 +22,7 @@ from rest_framework.viewsets import ModelViewSet
from structlog.stdlib import get_logger
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.api.pagination import Pagination
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.providers import ProviderSerializer
from authentik.core.api.used_by import UsedByMixin
@ -43,9 +44,12 @@ from authentik.rbac.filters import ObjectFilter
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"""
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):
@ -213,7 +217,8 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
return super().list(request)
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:
try:
@ -235,12 +240,14 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
if not should_cache:
allowed_applications = self._get_allowed_applications(paginated_apps)
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:
LOGGER.debug("Caching allowed application list")
LOGGER.debug("Caching allowed application list", page=paginator.page.number)
allowed_applications = self._get_allowed_applications(paginated_apps)
cache.set(
user_app_cache_key(self.request.user.pk),
user_app_cache_key(self.request.user.pk, paginator.page.number),
allowed_applications,
timeout=86400,
)

View File

@ -1,6 +1,8 @@
"""Events middleware"""
from collections.abc import Callable
from contextlib import contextmanager
from contextvars import ContextVar
from functools import partial
from threading import Thread
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:
"""Return true if operation on `model` should be logged"""
@ -44,6 +49,28 @@ def should_log_m2m(model: Model) -> bool:
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):
"""Create Event in background thread"""
@ -158,6 +185,10 @@ class AuditMiddleware:
"""Signal handler for all object's post_save"""
if not should_log_model(instance):
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
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"""
if not should_log_model(instance): # pragma: no cover
return
if _CTX_IGNORE.get():
return
if _new_user := _CTX_OVERWRITE_USER.get():
user = _new_user
EventNewThread(
EventAction.MODEL_DELETED,
@ -184,6 +219,10 @@ class AuditMiddleware:
return
if not should_log_m2m(instance):
return
if _CTX_IGNORE.get():
return
if _new_user := _CTX_OVERWRITE_USER.get():
user = _new_user
EventNewThread(
EventAction.MODEL_UPDATED,

View File

@ -5,7 +5,9 @@ from rest_framework.test import APITestCase
from authentik.core.models import Application
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.lib.generators import generate_id
class TestEventsMiddleware(APITestCase):
@ -15,35 +17,74 @@ class TestEventsMiddleware(APITestCase):
super().setUp()
self.user = create_test_admin_user()
self.client.force_login(self.user)
Event.objects.all().delete()
def test_create(self):
"""Test model creation event"""
uid = generate_id()
self.client.post(
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(
Event.objects.filter(
action=EventAction.MODEL_CREATED,
context__model__model_name="application",
context__model__app="authentik_core",
context__model__name="test-create",
context__model__name=uid,
).exists()
)
def test_delete(self):
"""Test model creation event"""
Application.objects.create(name="test-delete", slug="test-delete")
self.client.delete(
reverse("authentik_api:application-detail", kwargs={"slug": "test-delete"})
)
uid = generate_id()
Application.objects.create(name=uid, slug=uid)
self.client.delete(reverse("authentik_api:application-detail", kwargs={"slug": uid}))
self.assertFalse(Application.objects.filter(name="test").exists())
self.assertTrue(
Event.objects.filter(
action=EventAction.MODEL_DELETED,
context__model__model_name="application",
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()
)

View File

@ -31,6 +31,7 @@ from authentik.core.models import (
User,
UserTypes,
)
from authentik.events.middleware import audit_ignore
from authentik.events.models import Event, EventAction
from authentik.events.signals import get_login_event
from authentik.flows.planner import PLAN_CONTEXT_APPLICATION
@ -465,6 +466,7 @@ class TokenParams:
def __create_user_from_jwt(self, token: dict[str, Any], app: Application, source: OAuthSource):
"""Create user from JWT"""
with audit_ignore():
self.user, created = User.objects.update_or_create(
username=f"{self.provider.name}-{token.get('sub')}",
defaults={
@ -472,7 +474,9 @@ class TokenParams:
USER_ATTRIBUTE_GENERATED: True,
},
"last_login": timezone.now(),
"name": f"Autogenerated user from application {app.name} (client credentials JWT)",
"name": (
f"Autogenerated user from application {app.name} (client credentials JWT)"
),
"path": source.get_user_path(),
"type": UserTypes.SERVICE_ACCOUNT,
},

View File

@ -21,6 +21,7 @@ from webauthn.helpers.structs import UserVerificationRequirement
from authentik.core.api.utils import JSONDictField, PassiveSerializer
from authentik.core.models import Application, User
from authentik.core.signals import login_failed
from authentik.events.middleware import audit_ignore
from authentik.events.models import Event, EventAction
from authentik.flows.stage import StageView
from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE
@ -167,6 +168,7 @@ def validate_challenge_webauthn(data: dict, stage_view: StageView, user: User) -
)
raise ValidationError("Assertion failed") from exc
with audit_ignore():
device.set_sign_count(authentication_verification.new_sign_count)
return device

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -2251,6 +2251,19 @@ msgstr ""
msgid "WebAuthn Devices"
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
msgid "Public key, acquired your captcha Provider."
msgstr ""

View File

@ -6,7 +6,7 @@
"": {
"name": "@goauthentik/web-tests",
"dependencies": {
"chromedriver": "^123.0.1"
"chromedriver": "^123.0.2"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
@ -2084,9 +2084,9 @@
}
},
"node_modules/chromedriver": {
"version": "123.0.1",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.1.tgz",
"integrity": "sha512-YQUIP/zdlzDIRCZNCv6rEVDSY4RAxo/tDL0OiGPPuai+z8unRNqJr/9V6XTBypVFyDheXNalKt9QxEqdMPuLAQ==",
"version": "123.0.2",
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-123.0.2.tgz",
"integrity": "sha512-Kx0r/IGULm7eciaUtX/OKaFbdBdHRDSguiV1Q4zuQncz11gvymDdMtELa7ppk+kTL5113NLPud92nuIMNTRhww==",
"hasInstallScript": true,
"dependencies": {
"@testim/chrome-version": "^1.1.4",

View File

@ -32,6 +32,6 @@
"node": ">=20"
},
"dependencies": {
"chromedriver": "^123.0.1"
"chromedriver": "^123.0.2"
}
}

View File

@ -575,7 +575,11 @@ export class SAMLProviderViewPage extends AKElement {
</dt>
<dd class="pf-c-description-list__description">
<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>
</dd>
</div>`;

View File

@ -2,7 +2,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { me } from "@goauthentik/common/users";
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import { localized, msg } from "@lit/localize";
import { html } from "lit";
@ -25,6 +24,8 @@ import type { PageUIConfig } from "./types";
*
*/
const coreApi = () => new CoreApi(DEFAULT_CONFIG);
@localized()
@customElement("ak-library")
export class LibraryPage extends AKElement {
@ -35,15 +36,13 @@ export class LibraryPage extends AKElement {
isAdmin = false;
@state()
apps!: PaginatedResponse<Application>;
apps: Application[] = [];
@state()
uiConfig: PageUIConfig;
constructor() {
super();
const applicationListFetch = new CoreApi(DEFAULT_CONFIG).coreApplicationsList({});
const meFetch = me();
const uiConfig = rootInterface()?.uiConfig;
if (!uiConfig) {
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
@ -55,22 +54,41 @@ export class LibraryPage extends AKElement {
searchEnabled: uiConfig.enabledFeatures.search,
};
Promise.allSettled([applicationListFetch, meFetch]).then(
([applicationListStatus, meStatus]) => {
if (meStatus.status === "rejected") {
throw new Error(
`Could not determine status of user. Reason: ${meStatus.reason}`,
);
}
if (applicationListStatus.status === "rejected") {
throw new Error(
`Could not retrieve list of applications. Reason: ${applicationListStatus.reason}`,
);
}
this.isAdmin = meStatus.value.user.isSuperuser;
this.apps = applicationListStatus.value;
Promise.all([this.fetchApplications(), me()]).then(([applications, meStatus]) => {
this.isAdmin = meStatus.user.isSuperuser;
this.apps = applications;
this.ready = true;
});
}
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}`);
}
return [...acc, ...result.value.results];
},
[...applicationListFetch.results],
);
}

View File

@ -1,7 +1,6 @@
import { groupBy } from "@goauthentik/common/utils";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
import "@goauthentik/user/LibraryApplication";
import { msg } from "@lit/localize";
@ -42,8 +41,8 @@ export class LibraryPage extends AKElement {
@property({ attribute: "isadmin", type: Boolean })
isAdmin = false;
@property({ attribute: false })
apps!: PaginatedResponse<Application>;
@property({ attribute: false, type: Array })
apps!: Application[];
@property({ attribute: false })
uiConfig!: PageUIConfig;
@ -66,7 +65,7 @@ export class LibraryPage extends AKElement {
connectedCallback() {
super.connectedCallback();
this.filteredApps = this.apps?.results;
this.filteredApps = this.apps;
if (this.filteredApps === undefined) {
throw new Error(
"Application.results should never be undefined when passed to the Library Page.",
@ -89,7 +88,7 @@ export class LibraryPage extends AKElement {
event.stopPropagation();
const apps = event.detail.apps;
this.selectedApp = undefined;
this.filteredApps = this.apps.results;
this.filteredApps = this.apps;
if (apps.length > 0) {
this.selectedApp = apps[0];
this.filteredApps = event.detail.apps;
@ -132,7 +131,7 @@ export class LibraryPage extends AKElement {
}
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() {

View File

@ -6475,6 +6475,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -6744,6 +6744,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -6391,6 +6391,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -8504,6 +8504,18 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -8332,6 +8332,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -8175,6 +8175,18 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -6596,6 +6596,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -8448,4 +8448,16 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body></file></xliff>

View File

@ -6384,6 +6384,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -5302,6 +5302,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>
</xliff>

View File

@ -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">
<body>
<trans-unit id="s4caed5b7a7e5d89b">
@ -596,9 +596,9 @@
</trans-unit>
<trans-unit id="saa0e2675da69651b">
<source>The URL &quot;<x id="0" equiv-text="${this.url}"/>&quot; was not found.</source>
<target>未找到 URL &quot;
<x id="0" equiv-text="${this.url}"/>&quot;。</target>
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
<target>未找到 URL "
<x id="0" equiv-text="${this.url}"/>"。</target>
</trans-unit>
<trans-unit id="s58cd9c2fe836d9c6">
@ -1040,8 +1040,8 @@
</trans-unit>
<trans-unit id="sa8384c9c26731f83">
<source>To allow any redirect URI, set this value to &quot;.*&quot;. Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 &quot;.*&quot;。请注意这可能带来的安全影响。</target>
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
<target>要允许任何重定向 URI请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
</trans-unit>
<trans-unit id="s55787f4dfcdce52b">
@ -1782,8 +1782,8 @@
</trans-unit>
<trans-unit id="sa90b7809586c35ce">
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon &quot;fa-test&quot;.</source>
<target>输入完整 URL、相对路径或者使用 'fa://fa-test' 来使用 Font Awesome 图标 &quot;fa-test&quot;。</target>
<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>
</trans-unit>
<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 id="s76768bebabb7d543">
<source>Field which contains members of a group. Note that if using the &quot;memberUid&quot; 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>包含组成员的字段。请注意,如果使用 &quot;memberUid&quot; 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
<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>
</trans-unit>
<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 id="s7b1fba26d245cb1c">
<source>When using an external logging solution for archiving, this can be set to &quot;minutes=5&quot;.</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 &quot;minutes=5&quot;。</target>
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
</trans-unit>
<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 id="sa95a538bfbb86111">
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> &quot;<x id="1" equiv-text="${this.obj?.name}"/>&quot;?</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>您确定要更新
<x id="0" equiv-text="${this.objectLabel}"/>&quot;
<x id="1" equiv-text="${this.obj?.name}"/>&quot; 吗?</target>
<x id="0" equiv-text="${this.objectLabel}"/>"
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
</trans-unit>
<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 id="sdf1d8edef27236f0">
<source>A &quot;roaming&quot; authenticator, like a YubiKey</source>
<source>A "roaming" authenticator, like a YubiKey</source>
<target>像 YubiKey 这样的“漫游”身份验证器</target>
</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 id="s2d5f69929bb7221d">
<source><x id="0" equiv-text="${prompt.name}"/> (&quot;<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;, 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>
<x id="0" equiv-text="${prompt.name}"/>&quot;
<x id="1" equiv-text="${prompt.fieldKey}"/>&quot;,类型为
<x id="0" equiv-text="${prompt.name}"/>"
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
<x id="2" equiv-text="${prompt.type}"/></target>
</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 id="s1608b2f94fa0dbd4">
<source>If set to a duration above 0, the user will have the option to choose to &quot;stay signed in&quot;, 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>
</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>
</trans-unit>
<trans-unit id="s824e0943a7104668">
<source>This user will be added to the group &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</source>
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
<target>此用户将会被添加到组 &amp;quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&amp;quot;。</target>
</trans-unit>
<trans-unit id="s62e7f6ed7d9cb3ca">
@ -8523,6 +8523,18 @@ Bindings to groups/users are checked against the user of the event.</source>
<trans-unit id="sc7d071fb5cc1f6bf">
<source>A selection is required</source>
<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>
</body>
</file>

View File

@ -6432,6 +6432,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -8293,6 +8293,18 @@ Bindings to groups/users are checked against the user of the event.</source>
</trans-unit>
<trans-unit id="sc7d071fb5cc1f6bf">
<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>
</body>
</file>

View File

@ -27,7 +27,7 @@ cd /bitnami/postgresql/
# Set the postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable
export PGPASSWORD=$POSTGRES_POSTGRES_PASSWORD
# 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
@ -88,7 +88,7 @@ Run the following commands to restore the data:
cd /bitnami/postgresql/
# Set the Postgres password based on the `POSTGRES_POSTGRES_PASSWORD` environment variable.
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.

View File

@ -33,7 +33,7 @@
"@docusaurus/module-type-aliases": "3.2.1",
"@docusaurus/tsconfig": "3.2.1",
"@docusaurus/types": "3.2.1",
"@types/react": "^18.2.74",
"@types/react": "^18.2.75",
"prettier": "3.2.5",
"typescript": "~5.4.4"
},
@ -3902,9 +3902,9 @@
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA=="
},
"node_modules/@types/react": {
"version": "18.2.74",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz",
"integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==",
"version": "18.2.75",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.75.tgz",
"integrity": "sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"

View File

@ -52,7 +52,7 @@
"@docusaurus/module-type-aliases": "3.2.1",
"@docusaurus/tsconfig": "3.2.1",
"@docusaurus/types": "3.2.1",
"@types/react": "^18.2.74",
"@types/react": "^18.2.75",
"prettier": "3.2.5",
"typescript": "~5.4.4"
},