diff --git a/Dockerfile b/Dockerfile index d00c03b90f..eaf287b807 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index 413e7777a9..eddad64bf8 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -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, ) diff --git a/authentik/events/middleware.py b/authentik/events/middleware.py index 2b4705f3a3..eec473202a 100644 --- a/authentik/events/middleware.py +++ b/authentik/events/middleware.py @@ -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, diff --git a/authentik/events/tests/test_middleware.py b/authentik/events/tests/test_middleware.py index 1bd667d06e..906deb030d 100644 --- a/authentik/events/tests/test_middleware.py +++ b/authentik/events/tests/test_middleware.py @@ -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() ) diff --git a/authentik/providers/oauth2/views/token.py b/authentik/providers/oauth2/views/token.py index 464df72636..ce535f9905 100644 --- a/authentik/providers/oauth2/views/token.py +++ b/authentik/providers/oauth2/views/token.py @@ -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,22 +466,25 @@ class TokenParams: def __create_user_from_jwt(self, token: dict[str, Any], app: Application, source: OAuthSource): """Create user from JWT""" - self.user, created = User.objects.update_or_create( - username=f"{self.provider.name}-{token.get('sub')}", - defaults={ - "attributes": { - USER_ATTRIBUTE_GENERATED: True, + with audit_ignore(): + self.user, created = User.objects.update_or_create( + username=f"{self.provider.name}-{token.get('sub')}", + defaults={ + "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)", - "path": source.get_user_path(), - "type": UserTypes.SERVICE_ACCOUNT, - }, - ) - exp = token.get("exp") - if created and exp: - 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") diff --git a/authentik/stages/authenticator_validate/challenge.py b/authentik/stages/authenticator_validate/challenge.py index 38c7bb28ae..60a6786c52 100644 --- a/authentik/stages/authenticator_validate/challenge.py +++ b/authentik/stages/authenticator_validate/challenge.py @@ -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,7 +168,8 @@ def validate_challenge_webauthn(data: dict, stage_view: StageView, user: User) - ) 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 diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 67c78e425a..904bd1d24f 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: LANGUAGE \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 "" diff --git a/tests/wdio/package-lock.json b/tests/wdio/package-lock.json index da6633335a..84a8e87697 100644 --- a/tests/wdio/package-lock.json +++ b/tests/wdio/package-lock.json @@ -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", diff --git a/tests/wdio/package.json b/tests/wdio/package.json index 8f7d3c6996..cd29c85cca 100644 --- a/tests/wdio/package.json +++ b/tests/wdio/package.json @@ -32,6 +32,6 @@ "node": ">=20" }, "dependencies": { - "chromedriver": "^123.0.1" + "chromedriver": "^123.0.2" } } diff --git a/web/src/admin/providers/saml/SAMLProviderViewPage.ts b/web/src/admin/providers/saml/SAMLProviderViewPage.ts index dd00547b12..df9f16ef96 100644 --- a/web/src/admin/providers/saml/SAMLProviderViewPage.ts +++ b/web/src/admin/providers/saml/SAMLProviderViewPage.ts @@ -575,7 +575,11 @@ export class SAMLProviderViewPage extends AKElement {
-
    +
      + ${attr.Value.map((value) => { + return html`
    • ${value}
    • `; + })} +
    `; diff --git a/web/src/user/LibraryPage/LibraryPage.ts b/web/src/user/LibraryPage/LibraryPage.ts index cdd54f8f72..01ab26c718 100644 --- a/web/src/user/LibraryPage/LibraryPage.ts +++ b/web/src/user/LibraryPage/LibraryPage.ts @@ -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; + 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}`, - ); + Promise.all([this.fetchApplications(), me()]).then(([applications, meStatus]) => { + this.isAdmin = meStatus.user.isSuperuser; + this.apps = applications; + this.ready = true; + }); + } + + async fetchApplications(): Promise { + 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") { - 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; + return [...acc, ...result.value.results]; }, + [...applicationListFetch.results], ); } diff --git a/web/src/user/LibraryPage/LibraryPageImpl.ts b/web/src/user/LibraryPage/LibraryPageImpl.ts index 76d82793c1..1892e499f9 100644 --- a/web/src/user/LibraryPage/LibraryPageImpl.ts +++ b/web/src/user/LibraryPage/LibraryPageImpl.ts @@ -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; + @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``; + return html``; } render() { diff --git a/web/xliff/de.xlf b/web/xliff/de.xlf index cacbd40d1e..429b65cb92 100644 --- a/web/xliff/de.xlf +++ b/web/xliff/de.xlf @@ -6475,6 +6475,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/en.xlf b/web/xliff/en.xlf index 478b61a68c..9f6d9bcd30 100644 --- a/web/xliff/en.xlf +++ b/web/xliff/en.xlf @@ -6744,6 +6744,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/es.xlf b/web/xliff/es.xlf index 8a2023d6e5..6afd3320ef 100644 --- a/web/xliff/es.xlf +++ b/web/xliff/es.xlf @@ -6391,6 +6391,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/fr.xlf b/web/xliff/fr.xlf index dd8cb6180b..03c309e551 100644 --- a/web/xliff/fr.xlf +++ b/web/xliff/fr.xlf @@ -8504,6 +8504,18 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/ko.xlf b/web/xliff/ko.xlf index ff2c9d0a7d..e66fb6f61b 100644 --- a/web/xliff/ko.xlf +++ b/web/xliff/ko.xlf @@ -8332,6 +8332,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/nl.xlf b/web/xliff/nl.xlf index e11d30ad21..6d58c2bcf6 100644 --- a/web/xliff/nl.xlf +++ b/web/xliff/nl.xlf @@ -8175,6 +8175,18 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/pl.xlf b/web/xliff/pl.xlf index 009cdc4ff7..b5ff9a6e5f 100644 --- a/web/xliff/pl.xlf +++ b/web/xliff/pl.xlf @@ -6596,6 +6596,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/pseudo-LOCALE.xlf b/web/xliff/pseudo-LOCALE.xlf index a5d785d1b0..5a7cc37552 100644 --- a/web/xliff/pseudo-LOCALE.xlf +++ b/web/xliff/pseudo-LOCALE.xlf @@ -8448,4 +8448,16 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. + diff --git a/web/xliff/tr.xlf b/web/xliff/tr.xlf index 8f8e18bfed..80ba4a88b1 100644 --- a/web/xliff/tr.xlf +++ b/web/xliff/tr.xlf @@ -6384,6 +6384,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/zh-CN.xlf b/web/xliff/zh-CN.xlf index f25b697682..ffe8205a8e 100644 --- a/web/xliff/zh-CN.xlf +++ b/web/xliff/zh-CN.xlf @@ -5302,6 +5302,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. + diff --git a/web/xliff/zh-Hans.xlf b/web/xliff/zh-Hans.xlf index 682abe4ad2..53bc5d6bb9 100644 --- a/web/xliff/zh-Hans.xlf +++ b/web/xliff/zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -596,9 +596,9 @@ - The URL "" was not found. - 未找到 URL " - "。 + The URL "" was not found. + 未找到 URL " + "。 @@ -1040,8 +1040,8 @@ - To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. - 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 + To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have. + 要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。 @@ -1782,8 +1782,8 @@ - Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". - 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 + Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test". + 输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。 @@ -2961,8 +2961,8 @@ doesn't pass when either or both of the selected options are equal or above the - 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,...' - 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' + 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,...' + 包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...' @@ -3739,8 +3739,8 @@ doesn't pass when either or both of the selected options are equal or above the - When using an external logging solution for archiving, this can be set to "minutes=5". - 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 + When using an external logging solution for archiving, this can be set to "minutes=5". + 使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。 @@ -3916,10 +3916,10 @@ doesn't pass when either or both of the selected options are equal or above the - Are you sure you want to update ""? + Are you sure you want to update ""? 您确定要更新 - " - " 吗? + " + " 吗? @@ -5000,7 +5000,7 @@ doesn't pass when either or both of the selected options are equal or above the - A "roaming" authenticator, like a YubiKey + A "roaming" authenticator, like a YubiKey 像 YubiKey 这样的“漫游”身份验证器 @@ -5335,10 +5335,10 @@ doesn't pass when either or both of the selected options are equal or above the - ("", of type ) + ("", of type ) - (" - ",类型为 + (" + ",类型为 @@ -5387,7 +5387,7 @@ doesn't pass when either or both of the selected options are equal or above the - 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. + 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. 如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。 @@ -7839,7 +7839,7 @@ Bindings to groups/users are checked against the user of the event. 成功创建用户并添加到组 - This user will be added to the group "". + This user will be added to the group "". 此用户将会被添加到组 &quot;&quot;。 @@ -8523,7 +8523,19 @@ Bindings to groups/users are checked against the user of the event. A selection is required 需要进行选择 + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. - \ No newline at end of file + diff --git a/web/xliff/zh-Hant.xlf b/web/xliff/zh-Hant.xlf index 3acef706ff..917771ecd6 100644 --- a/web/xliff/zh-Hant.xlf +++ b/web/xliff/zh-Hant.xlf @@ -6432,6 +6432,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/web/xliff/zh_TW.xlf b/web/xliff/zh_TW.xlf index 62695991c3..398cc40220 100644 --- a/web/xliff/zh_TW.xlf +++ b/web/xliff/zh_TW.xlf @@ -8293,6 +8293,18 @@ Bindings to groups/users are checked against the user of the event. A selection is required + + + Device type restrictions + + + Available Device types + + + Selected Device types + + + Optionally restrict which WebAuthn device types may be used. When no device types are selected, all devices are allowed. diff --git a/website/docs/troubleshooting/postgres/upgrade_kubernetes.md b/website/docs/troubleshooting/postgres/upgrade_kubernetes.md index 5aac942357..be47140813 100644 --- a/website/docs/troubleshooting/postgres/upgrade_kubernetes.md +++ b/website/docs/troubleshooting/postgres/upgrade_kubernetes.md @@ -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. diff --git a/website/package-lock.json b/website/package-lock.json index 2d0930f652..6785cab4d6 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -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" diff --git a/website/package.json b/website/package.json index 4bcd7a969c..fa41024f33 100644 --- a/website/package.json +++ b/website/package.json @@ -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" },