Merge branch 'main' into new-push

This commit is contained in:
Silvan 2024-12-03 13:37:22 +01:00 committed by GitHub
commit ed04d09db9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 5662 additions and 467 deletions

View File

@ -12,5 +12,8 @@ If you are using Zitadel, please consider adding yourself as a user with a quick
| ----------------------- | -------------------------------------------------------------------- | ----------------------------------------------- | | ----------------------- | -------------------------------------------------------------------- | ----------------------------------------------- |
| Zitadel | [@fforootd](https://github.com/fforootd) (and many more) | Zitadel Cloud makes heavy use of of Zitadel ;-) | | Zitadel | [@fforootd](https://github.com/fforootd) (and many more) | Zitadel Cloud makes heavy use of of Zitadel ;-) |
| Rawkode Academy | [@RawkodeAcademy](https://github.com/RawkodeAcademy) | Rawkode Academy Platform & Zulip use Zitadel for all user and M2M authentication | | Rawkode Academy | [@RawkodeAcademy](https://github.com/RawkodeAcademy) | Rawkode Academy Platform & Zulip use Zitadel for all user and M2M authentication |
| devOS: Sanity Edition | [@devOS-Sanity-Edition](https://github.com/devOS-Sanity-Edition) | Uses SSO Auth for every piece of our internal and external infrastructure |
| CNAP.tech | [@cnap-tech](https://github.com/cnap-tech) | Using Zitadel for authentication and authorization in cloud-native applications |
| Minekube | [@minekube](https://github.com/minekube) | Leveraging Zitadel for secure user authentication in gaming infrastructure |
| Organization Name | contact@example.com | Description of how they use Zitadel | | Organization Name | contact@example.com | Description of how they use Zitadel |
| Individual Name | contact@example.com | Description of how they use Zitadel | | Individual Name | contact@example.com | Description of how they use Zitadel |

View File

@ -17,6 +17,7 @@ import localeRu from '@angular/common/locales/ru';
import localeNl from '@angular/common/locales/nl'; import localeNl from '@angular/common/locales/nl';
import localeSv from '@angular/common/locales/sv'; import localeSv from '@angular/common/locales/sv';
import localeHu from '@angular/common/locales/hu'; import localeHu from '@angular/common/locales/hu';
import localeKo from '@angular/common/locales/ko';
import { APP_INITIALIZER, NgModule } from '@angular/core'; import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MatNativeDateModule } from '@angular/material/core'; import { MatNativeDateModule } from '@angular/material/core';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
@ -108,6 +109,8 @@ registerLocaleData(localeSv);
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/sv.json')); i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/sv.json'));
registerLocaleData(localeHu); registerLocaleData(localeHu);
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/hu.json')); i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/hu.json'));
registerLocaleData(localeKo);
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/ko.json'));
export class WebpackTranslateLoader implements TranslateLoader { export class WebpackTranslateLoader implements TranslateLoader {
getTranslation(lang: string): Observable<any> { getTranslation(lang: string): Observable<any> {

View File

@ -16,6 +16,7 @@ export const supportedLanguages = [
'nl', 'nl',
'sv', 'sv',
'hu', 'hu',
'ko',
]; ];
export const supportedLanguagesRegexp: RegExp = /de|en|es|fr|id|it|ja|pl|zh|bg|pt|mk|cs|ru|nl|sv|hu/; export const supportedLanguagesRegexp: RegExp = /de|en|es|fr|id|it|ja|pl|zh|bg|pt|mk|cs|ru|nl|sv|hu|ko/;
export const fallbackLanguage: string = 'en'; export const fallbackLanguage: string = 'en';

View File

@ -1383,7 +1383,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1620,7 +1621,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Проверката на имейл е извършена", "emailVerificationDoneText": "Проверката на имейл е извършена",
@ -1720,8 +1722,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Разрешено е конвенционалното влизане с потребителско име и парола.", "ALLOWUSERNAMEPASSWORD_DESC": "Разрешено е конвенционалното влизане с потребителско име и парола.",
"ALLOWEXTERNALIDP_DESC": "Входът е разрешен за основните доставчици на самоличност", "ALLOWEXTERNALIDP_DESC": "Входът е разрешен за основните доставчици на самоличност",
"ALLOWREGISTER_DESC": "Ако опцията е избрана, в входа се появява допълнителна стъпка за регистрация на потребител.", "ALLOWREGISTER_DESC": "Ако опцията е избрана, в входа се появява допълнителна стъпка за регистрация на потребител.",
"FORCEMFA": "Сила MFA", "FORCEMFA": "Наложи MFA за всички потребители",
"FORCEMFALOCALONLY": "Принудително MFA за локални потребители", "FORCEMFALOCALONLY": "Наложи MFA само за локално автентифицирани потребители",
"FORCEMFALOCALONLY_DESC": "Ако е избрана опцията, локалните удостоверени потребители трябва да конфигурират втори фактор за влизане.", "FORCEMFALOCALONLY_DESC": "Ако е избрана опцията, локалните удостоверени потребители трябва да конфигурират втори фактор за влизане.",
"HIDEPASSWORDRESET_DESC": "Ако опцията е избрана, потребителят не може да нулира паролата си в процеса на влизане.", "HIDEPASSWORDRESET_DESC": "Ако опцията е избрана, потребителят не може да нулира паролата си в процеса на влизане.",
"HIDELOGINNAMESUFFIX": "Скриване на суфикса на името за влизане", "HIDELOGINNAMESUFFIX": "Скриване на суфикса на името за влизане",
@ -2559,7 +2561,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Добавяне на мениджър", "ADD": "Добавяне на мениджър",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Ověření e-mailu dokončeno", "emailVerificationDoneText": "Ověření e-mailu dokončeno",
@ -1722,8 +1724,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Je povoleno klasické přihlášení s uživatelským jménem a heslem.", "ALLOWUSERNAMEPASSWORD_DESC": "Je povoleno klasické přihlášení s uživatelským jménem a heslem.",
"ALLOWEXTERNALIDP_DESC": "Přihlášení je povoleno pro níže uvedené poskytovatele identity.", "ALLOWEXTERNALIDP_DESC": "Přihlášení je povoleno pro níže uvedené poskytovatele identity.",
"ALLOWREGISTER_DESC": "Pokud je možnost vybrána, objeví se při přihlášení další krok pro registraci uživatele.", "ALLOWREGISTER_DESC": "Pokud je možnost vybrána, objeví se při přihlášení další krok pro registraci uživatele.",
"FORCEMFA": "Vynutit MFA", "FORCEMFA": "Vynuti MFA pro všechny uživatele",
"FORCEMFALOCALONLY": "Vynutit MFA pouze pro lokálně ověřené uživatele", "FORCEMFALOCALONLY": "Vynutit MFA pouze pro místně ověřené uživatele",
"FORCEMFALOCALONLY_DESC": "Pokud je možnost vybrána, lokálně ověření uživatelé musí pro přihlášení nastavit druhý faktor.", "FORCEMFALOCALONLY_DESC": "Pokud je možnost vybrána, lokálně ověření uživatelé musí pro přihlášení nastavit druhý faktor.",
"HIDEPASSWORDRESET_DESC": "Pokud je možnost vybrána, uživatel nemůže během přihlašovacího procesu resetovat své heslo.", "HIDEPASSWORDRESET_DESC": "Pokud je možnost vybrána, uživatel nemůže během přihlašovacího procesu resetovat své heslo.",
"HIDELOGINNAMESUFFIX": "Skrýt příponu přihlašovacího jména", "HIDELOGINNAMESUFFIX": "Skrýt příponu přihlašovacího jména",
@ -2572,7 +2574,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Přidat manažera", "ADD": "Přidat manažera",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Email Verification erfolgreich", "emailVerificationDoneText": "Email Verification erfolgreich",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Der konventionelle Login mit Benutzername und Passwort wird erlaubt.", "ALLOWUSERNAMEPASSWORD_DESC": "Der konventionelle Login mit Benutzername und Passwort wird erlaubt.",
"ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identitätsanbieter erlaubt.", "ALLOWEXTERNALIDP_DESC": "Der Login wird für die darunter liegenden Identitätsanbieter erlaubt.",
"ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.", "ALLOWREGISTER_DESC": "Ist die Option gewählt, erscheint im Login ein zusätzlicher Schritt zum Registrieren eines Benutzers.",
"FORCEMFA": "MFA erzwingen", "FORCEMFA": "MFA für alle Benutzer erzwingen",
"FORCEMFALOCALONLY": "MFA für lokale Benutzer erzwingen", "FORCEMFALOCALONLY": "MFA nur für lokal authentifizierte Benutzer erzwingen",
"FORCEMFALOCALONLY_DESC": "Ist die Option gewählt, müssen lokal authentifizierte Benutzer einen zweiten Faktor für den Login verwenden.", "FORCEMFALOCALONLY_DESC": "Ist die Option gewählt, müssen lokal authentifizierte Benutzer einen zweiten Faktor für den Login verwenden.",
"HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.", "HIDEPASSWORDRESET_DESC": "Ist die Option gewählt, ist es nicht möglich im Login das Passwort zurück zusetzen via Passwort vergessen Link.",
"HIDELOGINNAMESUFFIX": "Loginname Suffix ausblenden", "HIDELOGINNAMESUFFIX": "Loginname Suffix ausblenden",
@ -2563,7 +2565,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Manager hinzufügen", "ADD": "Manager hinzufügen",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Email verification done", "emailVerificationDoneText": "Email verification done",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "The conventional login with user name and password is allowed.", "ALLOWUSERNAMEPASSWORD_DESC": "The conventional login with user name and password is allowed.",
"ALLOWEXTERNALIDP_DESC": "The login is allowed for the underlying identity providers", "ALLOWEXTERNALIDP_DESC": "The login is allowed for the underlying identity providers",
"ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.", "ALLOWREGISTER_DESC": "If the option is selected, an additional step for registering a user appears in the login.",
"FORCEMFA": "Force MFA", "FORCEMFA": "Force MFA for all users",
"FORCEMFALOCALONLY": "Force MFA for local authenticated users", "FORCEMFALOCALONLY": "Force MFA for local authenticated users only",
"FORCEMFALOCALONLY_DESC": "If the option is selected, local authenticated users have to configure a second factor for login.", "FORCEMFALOCALONLY_DESC": "If the option is selected, local authenticated users have to configure a second factor for login.",
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.", "HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
"HIDELOGINNAMESUFFIX": "Hide Loginname suffix", "HIDELOGINNAMESUFFIX": "Hide Loginname suffix",
@ -2588,7 +2590,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Add a Manager", "ADD": "Add a Manager",

View File

@ -1385,7 +1385,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1622,7 +1623,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Verificación de email realizada", "emailVerificationDoneText": "Verificación de email realizada",
@ -1722,8 +1724,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "El inicio de sesión convencional con nombre de usuario y contraseña está permitido.", "ALLOWUSERNAMEPASSWORD_DESC": "El inicio de sesión convencional con nombre de usuario y contraseña está permitido.",
"ALLOWEXTERNALIDP_DESC": "El inicio de sesión está permitido para los proveedores de identidad subyacentes", "ALLOWEXTERNALIDP_DESC": "El inicio de sesión está permitido para los proveedores de identidad subyacentes",
"ALLOWREGISTER_DESC": "Si esta opción es seleccionada, aparece un paso adicional durante el inicio de sesión para registrar un usuario.", "ALLOWREGISTER_DESC": "Si esta opción es seleccionada, aparece un paso adicional durante el inicio de sesión para registrar un usuario.",
"FORCEMFA": "Forzar MFA", "FORCEMFA": "Forzar MFA para todos los usuarios",
"FORCEMFALOCALONLY": "Forzar MFA para usuarios locales", "FORCEMFALOCALONLY": "Forzar MFA solo para usuarios autenticados localmente",
"FORCEMFALOCALONLY_DESC": "Si esta opción es seleccionada, los usuarios autenticados localmente tendrán que configurar un doble factor para iniciar sesión", "FORCEMFALOCALONLY_DESC": "Si esta opción es seleccionada, los usuarios autenticados localmente tendrán que configurar un doble factor para iniciar sesión",
"HIDEPASSWORDRESET_DESC": "Si esta opción es seleccionada, el usuario no podrá restablecer su contraseña en el proceso de inicio de sesión.", "HIDEPASSWORDRESET_DESC": "Si esta opción es seleccionada, el usuario no podrá restablecer su contraseña en el proceso de inicio de sesión.",
"HIDELOGINNAMESUFFIX": "Ocultar sufijo del nombre de inicio de sesión", "HIDELOGINNAMESUFFIX": "Ocultar sufijo del nombre de inicio de sesión",
@ -2560,7 +2562,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Añadir un Mánager", "ADD": "Añadir un Mánager",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Vérification de l'e-mail effectuée", "emailVerificationDoneText": "Vérification de l'e-mail effectuée",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "La connexion classique avec nom d'utilisateur et mot de passe est autorisée.", "ALLOWUSERNAMEPASSWORD_DESC": "La connexion classique avec nom d'utilisateur et mot de passe est autorisée.",
"ALLOWEXTERNALIDP_DESC": "La connexion est autorisée pour les fournisseurs d'identité sous-jacents", "ALLOWEXTERNALIDP_DESC": "La connexion est autorisée pour les fournisseurs d'identité sous-jacents",
"ALLOWREGISTER_DESC": "Si l'option est sélectionnée, une étape supplémentaire pour l'enregistrement d'un utilisateur apparaît dans la connexion.", "ALLOWREGISTER_DESC": "Si l'option est sélectionnée, une étape supplémentaire pour l'enregistrement d'un utilisateur apparaît dans la connexion.",
"FORCEMFA": "Forcer MFA", "FORCEMFA": "Forcer MFA pour tous les utilisateurs",
"FORCEMFALOCALONLY": "Forcer MFA pour les utilisateurs locaux", "FORCEMFALOCALONLY": "Forcer MFA uniquement pour les utilisateurs authentifiés localement",
"FORCEMFALOCALONLY_DESC": "Si l'option est sélectionnée, les utilisateurs locaux authentifiés doivent configurer un deuxième facteur pour la connexion.", "FORCEMFALOCALONLY_DESC": "Si l'option est sélectionnée, les utilisateurs locaux authentifiés doivent configurer un deuxième facteur pour la connexion.",
"HIDEPASSWORDRESET_DESC": "Si l'option est sélectionnée, l'utilisateur ne peut pas réinitialiser son mot de passe lors du processus de connexion.", "HIDEPASSWORDRESET_DESC": "Si l'option est sélectionnée, l'utilisateur ne peut pas réinitialiser son mot de passe lors du processus de connexion.",
"HIDELOGINNAMESUFFIX": "Masquer le suffixe du nom de connexion", "HIDELOGINNAMESUFFIX": "Masquer le suffixe du nom de connexion",
@ -2564,7 +2566,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Ajouter un responsable", "ADD": "Ajouter un responsable",

View File

@ -1384,7 +1384,8 @@
"nl": "Holland", "nl": "Holland",
"sv": "Svéd", "sv": "Svéd",
"id": "Indonéz", "id": "Indonéz",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1619,7 +1620,8 @@
"ru": "Orosz", "ru": "Orosz",
"nl": "Holland", "nl": "Holland",
"sv": "Svéd", "sv": "Svéd",
"id": "Indonéz" "id": "Indonéz",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "E-mail ellenőrzés kész", "emailVerificationDoneText": "E-mail ellenőrzés kész",
@ -1719,8 +1721,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "A hagyományos bejelentkezés felhasználónévvel és jelszóval engedélyezett.", "ALLOWUSERNAMEPASSWORD_DESC": "A hagyományos bejelentkezés felhasználónévvel és jelszóval engedélyezett.",
"ALLOWEXTERNALIDP_DESC": "A bejelentkezés engedélyezett az alapul szolgáló identitásszolgáltatóknál", "ALLOWEXTERNALIDP_DESC": "A bejelentkezés engedélyezett az alapul szolgáló identitásszolgáltatóknál",
"ALLOWREGISTER_DESC": "Ha ezt az opciót választod, egy további lépés jelenik meg a bejelentkezés során a felhasználói regisztrációhoz.", "ALLOWREGISTER_DESC": "Ha ezt az opciót választod, egy további lépés jelenik meg a bejelentkezés során a felhasználói regisztrációhoz.",
"FORCEMFA": "MFA kényszerítése", "FORCEMFA": "MFA kikényszerítése minden felhasználó számára",
"FORCEMFALOCALONLY": "Kényszerítsd az MFA-t a helyileg hitelesített felhasználókra", "FORCEMFALOCALONLY": "MFA kikényszerítése csak helyi hitelesített felhasználók számára",
"FORCEMFALOCALONLY_DESC": "Ha ezt az opciót választod, a helyileg hitelesített felhasználóknak be kell állítaniuk egy második faktor a bejelentkezéshez.", "FORCEMFALOCALONLY_DESC": "Ha ezt az opciót választod, a helyileg hitelesített felhasználóknak be kell állítaniuk egy második faktor a bejelentkezéshez.",
"HIDEPASSWORDRESET_DESC": "Ha ezt az opciót választod, a felhasználó nem tudja visszaállítani a jelszavát a bejelentkezési folyamat során.", "HIDEPASSWORDRESET_DESC": "Ha ezt az opciót választod, a felhasználó nem tudja visszaállítani a jelszavát a bejelentkezési folyamat során.",
"HIDELOGINNAMESUFFIX": "Bejelentkezési név utótag elrejtése", "HIDELOGINNAMESUFFIX": "Bejelentkezési név utótag elrejtése",

View File

@ -1262,7 +1262,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1486,7 +1487,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Verifikasi email selesai", "emailVerificationDoneText": "Verifikasi email selesai",
@ -1585,8 +1587,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Login konvensional dengan nama pengguna dan kata sandi diperbolehkan.", "ALLOWUSERNAMEPASSWORD_DESC": "Login konvensional dengan nama pengguna dan kata sandi diperbolehkan.",
"ALLOWEXTERNALIDP_DESC": "Login diperbolehkan untuk penyedia identitas yang mendasarinya", "ALLOWEXTERNALIDP_DESC": "Login diperbolehkan untuk penyedia identitas yang mendasarinya",
"ALLOWREGISTER_DESC": "Jika opsi ini dipilih, langkah tambahan untuk mendaftarkan pengguna akan muncul di login.", "ALLOWREGISTER_DESC": "Jika opsi ini dipilih, langkah tambahan untuk mendaftarkan pengguna akan muncul di login.",
"FORCEMFA": "Paksa MFA", "FORCEMFA": "Memaksa MFA untuk semua pengguna",
"FORCEMFALOCALONLY": "Paksa MFA untuk pengguna lokal yang diautentikasi", "FORCEMFALOCALONLY": "Memaksa MFA hanya untuk pengguna yang diautentikasi lokal",
"FORCEMFALOCALONLY_DESC": "Jika opsi ini dipilih, pengguna yang diautentikasi lokal harus mengonfigurasi faktor kedua untuk login.", "FORCEMFALOCALONLY_DESC": "Jika opsi ini dipilih, pengguna yang diautentikasi lokal harus mengonfigurasi faktor kedua untuk login.",
"HIDEPASSWORDRESET_DESC": "Jika opsi ini dipilih, pengguna tidak dapat mengatur ulang kata sandinya dalam proses login.", "HIDEPASSWORDRESET_DESC": "Jika opsi ini dipilih, pengguna tidak dapat mengatur ulang kata sandinya dalam proses login.",
"HIDELOGINNAMESUFFIX": "Sembunyikan akhiran Nama Login", "HIDELOGINNAMESUFFIX": "Sembunyikan akhiran Nama Login",
@ -2272,7 +2274,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Tambahkan Manajer", "ADD": "Tambahkan Manajer",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Verifica dell'e-mail terminata con successo.", "emailVerificationDoneText": "Verifica dell'e-mail terminata con successo.",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Autenticazione classica con nome utente e password è permessa.", "ALLOWUSERNAMEPASSWORD_DESC": "Autenticazione classica con nome utente e password è permessa.",
"ALLOWEXTERNALIDP_DESC": "Il login è permesso per gli IDP sottostanti", "ALLOWEXTERNALIDP_DESC": "Il login è permesso per gli IDP sottostanti",
"ALLOWREGISTER_DESC": "Se l'opzione è selezionata, nel login apparirà un passo aggiuntivo per la registrazione di un utente.", "ALLOWREGISTER_DESC": "Se l'opzione è selezionata, nel login apparirà un passo aggiuntivo per la registrazione di un utente.",
"FORCEMFA": "Forza MFA", "FORCEMFA": "Forzare MFA per tutti gli utenti",
"FORCEMFALOCALONLY": "Forza MFA per gli utenti locali", "FORCEMFALOCALONLY": "Forzare MFA solo per gli utenti autenticati localmente",
"FORCEMFALOCALONLY_DESC": "Se l'opzione è selezionata, gli utenti locali autenticati devono configurare un secondo fattore per l'accesso.", "FORCEMFALOCALONLY_DESC": "Se l'opzione è selezionata, gli utenti locali autenticati devono configurare un secondo fattore per l'accesso.",
"HIDEPASSWORDRESET_DESC": "Se l'opzione è selezionata, l'utente non può resettare la sua password nel interfaccia login.", "HIDEPASSWORDRESET_DESC": "Se l'opzione è selezionata, l'utente non può resettare la sua password nel interfaccia login.",
"HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente", "HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente",
@ -2564,7 +2566,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Aggiungi un manager", "ADD": "Aggiungi un manager",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1617,7 +1618,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "メール認証が完了しました", "emailVerificationDoneText": "メール認証が完了しました",
@ -1716,8 +1718,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "ユーザー名とパスワードを使用した従来のログインを許可します。", "ALLOWUSERNAMEPASSWORD_DESC": "ユーザー名とパスワードを使用した従来のログインを許可します。",
"ALLOWEXTERNALIDP_DESC": "基礎となるIDプロバイダーにログインを許可します。", "ALLOWEXTERNALIDP_DESC": "基礎となるIDプロバイダーにログインを許可します。",
"ALLOWREGISTER_DESC": "このオプションが選択されている場合、ユーザーを登録するための追加のステップがログインに表示されます。", "ALLOWREGISTER_DESC": "このオプションが選択されている場合、ユーザーを登録するための追加のステップがログインに表示されます。",
"FORCEMFA": "MFAを強制する", "FORCEMFA": "すべてのユーザーに MFA を強制する",
"FORCEMFALOCALONLY": "ローカル ユーザーに MFA を強制する", "FORCEMFALOCALONLY": "ローカル認証ユーザーのみに MFA を強制する",
"FORCEMFALOCALONLY_DESC": "オプションが選択されている場合、ローカル認証されたユーザーはログインの 2 番目の要素を構成する必要があります。", "FORCEMFALOCALONLY_DESC": "オプションが選択されている場合、ローカル認証されたユーザーはログインの 2 番目の要素を構成する必要があります。",
"HIDEPASSWORDRESET_DESC": "このオプションが選択されている場合、ユーザーはログイン過程ででパスワードをリセットできません。", "HIDEPASSWORDRESET_DESC": "このオプションが選択されている場合、ユーザーはログイン過程ででパスワードをリセットできません。",
"HIDELOGINNAMESUFFIX": "ログイン名の接尾辞を非表示にする", "HIDELOGINNAMESUFFIX": "ログイン名の接尾辞を非表示にする",
@ -2554,7 +2556,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "マネージャーを追加する", "ADD": "マネージャーを追加する",

File diff suppressed because it is too large Load Diff

View File

@ -1385,7 +1385,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1622,7 +1623,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Е-поштата е верифицирана", "emailVerificationDoneText": "Е-поштата е верифицирана",
@ -1722,8 +1724,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Дозволена е конвенционална најава со корисничко име и лозинка.", "ALLOWUSERNAMEPASSWORD_DESC": "Дозволена е конвенционална најава со корисничко име и лозинка.",
"ALLOWEXTERNALIDP_DESC": "Најавата е дозволена за поддржуваните IDPs", "ALLOWEXTERNALIDP_DESC": "Најавата е дозволена за поддржуваните IDPs",
"ALLOWREGISTER_DESC": "Доколку е избрана опцијата, се прикажува дополнителен чекор за регистрирање на корисник во најавата.", "ALLOWREGISTER_DESC": "Доколку е избрана опцијата, се прикажува дополнителен чекор за регистрирање на корисник во најавата.",
"FORCEMFA": "Задолжителна MFA", "FORCEMFA": "Наметнете MFA за сите корисници",
"FORCEMFALOCALONLY": "Force MFA за локални корисници", "FORCEMFALOCALONLY": "Наметнете MFA само за локално автентифицирани корисници",
"FORCEMFALOCALONLY_DESC": "Ако е избрана опцијата, локалните автентицирани корисници треба да конфигурираат втор фактор за најавување.", "FORCEMFALOCALONLY_DESC": "Ако е избрана опцијата, локалните автентицирани корисници треба да конфигурираат втор фактор за најавување.",
"HIDEPASSWORDRESET_DESC": "Доколку е избрана опцијата, корисникот нема да може да ја ресетира својата лозинка во процесот на најава.", "HIDEPASSWORDRESET_DESC": "Доколку е избрана опцијата, корисникот нема да може да ја ресетира својата лозинка во процесот на најава.",
"HIDELOGINNAMESUFFIX": "Сокриј го суфиксот на корисничкото име", "HIDELOGINNAMESUFFIX": "Сокриј го суфиксот на корисничкото име",
@ -2560,7 +2562,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Додај Менаџер", "ADD": "Додај Менаџер",

View File

@ -1620,7 +1620,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "E-mail verificatie voltooid", "emailVerificationDoneText": "E-mail verificatie voltooid",
@ -1720,8 +1721,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "De conventionele login met gebruikersnaam en wachtwoord is toegestaan.", "ALLOWUSERNAMEPASSWORD_DESC": "De conventionele login met gebruikersnaam en wachtwoord is toegestaan.",
"ALLOWEXTERNALIDP_DESC": "De login is toegestaan voor de onderliggende identiteitsproviders", "ALLOWEXTERNALIDP_DESC": "De login is toegestaan voor de onderliggende identiteitsproviders",
"ALLOWREGISTER_DESC": "Als de optie is geselecteerd, verschijnt er een extra stap voor het registreren van een gebruiker in het login proces.", "ALLOWREGISTER_DESC": "Als de optie is geselecteerd, verschijnt er een extra stap voor het registreren van een gebruiker in het login proces.",
"FORCEMFA": "Forceer MFA", "FORCEMFA": "MFA afdwingen voor alle gebruikers",
"FORCEMFALOCALONLY": "Forceer MFA voor lokaal geauthenticeerde gebruikers", "FORCEMFALOCALONLY": "MFA alleen afdwingen voor lokaal geverifieerde gebruikers",
"FORCEMFALOCALONLY_DESC": "Als de optie is geselecteerd, moeten lokaal geauthenticeerde gebruikers een tweede factor configureren voor login.", "FORCEMFALOCALONLY_DESC": "Als de optie is geselecteerd, moeten lokaal geauthenticeerde gebruikers een tweede factor configureren voor login.",
"HIDEPASSWORDRESET_DESC": "Als de optie is geselecteerd, kan de gebruiker zijn wachtwoord niet resetten in het login proces.", "HIDEPASSWORDRESET_DESC": "Als de optie is geselecteerd, kan de gebruiker zijn wachtwoord niet resetten in het login proces.",
"HIDELOGINNAMESUFFIX": "Verberg Inlognaam achtervoegsel", "HIDELOGINNAMESUFFIX": "Verberg Inlognaam achtervoegsel",
@ -2580,7 +2581,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Voeg een Manager toe", "ADD": "Voeg een Manager toe",

View File

@ -1383,7 +1383,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1620,7 +1621,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Weryfikacja adresu e-mail zakończona", "emailVerificationDoneText": "Weryfikacja adresu e-mail zakończona",
@ -1720,8 +1722,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Zwykłe logowanie za pomocą nazwy użytkownika i hasła jest dozwolone.", "ALLOWUSERNAMEPASSWORD_DESC": "Zwykłe logowanie za pomocą nazwy użytkownika i hasła jest dozwolone.",
"ALLOWEXTERNALIDP_DESC": "Logowanie jest dozwolone dla dostawców tożsamości podstawowych", "ALLOWEXTERNALIDP_DESC": "Logowanie jest dozwolone dla dostawców tożsamości podstawowych",
"ALLOWREGISTER_DESC": "Jeśli ta opcja jest zaznaczona, pojawi się dodatkowy krok rejestracji użytkownika w procesie logowania.", "ALLOWREGISTER_DESC": "Jeśli ta opcja jest zaznaczona, pojawi się dodatkowy krok rejestracji użytkownika w procesie logowania.",
"FORCEMFA": "Wymuś MFA", "FORCEMFA": "Wymuś MFA dla wszystkich użytkowników",
"FORCEMFALOCALONLY": "Wymuś MFA dla lokalnych użytkowników", "FORCEMFALOCALONLY": "Wymuś MFA tylko dla lokalnie uwierzytelnionych użytkowników",
"FORCEMFALOCALONLY_DESC": "Jeśli ta opcja jest zaznaczona, lokalni uwierzytelnieni użytkownicy muszą skonfigurować drugi czynnik logowania.", "FORCEMFALOCALONLY_DESC": "Jeśli ta opcja jest zaznaczona, lokalni uwierzytelnieni użytkownicy muszą skonfigurować drugi czynnik logowania.",
"HIDEPASSWORDRESET_DESC": "Jeśli ta opcja jest zaznaczona, użytkownik nie może zresetować swojego hasła w procesie logowania.", "HIDEPASSWORDRESET_DESC": "Jeśli ta opcja jest zaznaczona, użytkownik nie może zresetować swojego hasła w procesie logowania.",
"HIDELOGINNAMESUFFIX": "Ukryj sufiks nazwy użytkownika", "HIDELOGINNAMESUFFIX": "Ukryj sufiks nazwy użytkownika",
@ -2563,7 +2565,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Dodaj managera", "ADD": "Dodaj managera",

View File

@ -1385,7 +1385,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1622,7 +1623,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "Verificação de email concluída", "emailVerificationDoneText": "Verificação de email concluída",
@ -1722,7 +1724,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.", "ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.",
"ALLOWEXTERNALIDP_DESC": "O login é permitido para os provedores de identidade subjacentes", "ALLOWEXTERNALIDP_DESC": "O login é permitido para os provedores de identidade subjacentes",
"ALLOWREGISTER_DESC": "Se a opção estiver selecionada, uma etapa adicional para registrar um usuário aparecerá no login.", "ALLOWREGISTER_DESC": "Se a opção estiver selecionada, uma etapa adicional para registrar um usuário aparecerá no login.",
"FORCEMFA": "Forçar MFA", "FORCEMFA": "Forçar MFA para todos os utilizadores",
"FORCEMFALOCALONLY": "Forçar MFA apenas para utilizadores autenticados localmente",
"HIDEPASSWORDRESET_DESC": "Se a opção estiver selecionada, o usuário não poderá redefinir sua senha no processo de login.", "HIDEPASSWORDRESET_DESC": "Se a opção estiver selecionada, o usuário não poderá redefinir sua senha no processo de login.",
"HIDELOGINNAMESUFFIX": "Ocultar sufixo do nome de login", "HIDELOGINNAMESUFFIX": "Ocultar sufixo do nome de login",
"HIDELOGINNAMESUFFIX_DESC": "Oculta o sufixo do nome de login na interface de login", "HIDELOGINNAMESUFFIX_DESC": "Oculta o sufixo do nome de login na interface de login",
@ -2558,7 +2561,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Adicionar um Gerente", "ADD": "Adicionar um Gerente",

View File

@ -1428,7 +1428,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1677,7 +1678,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"LOCALE": "Код языка", "LOCALE": "Код языка",
"LOCALES": { "LOCALES": {
@ -1792,7 +1794,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Допускается стандартный вход с именем пользователя и паролем.", "ALLOWUSERNAMEPASSWORD_DESC": "Допускается стандартный вход с именем пользователя и паролем.",
"ALLOWEXTERNALIDP_DESC": "Вход разрешён для основных поставщиков идентификационных данных.", "ALLOWEXTERNALIDP_DESC": "Вход разрешён для основных поставщиков идентификационных данных.",
"ALLOWREGISTER_DESC": "Если данный параметр выбран, при входе в систему появляется дополнительный шаг для регистрации пользователя.", "ALLOWREGISTER_DESC": "Если данный параметр выбран, при входе в систему появляется дополнительный шаг для регистрации пользователя.",
"FORCEMFA": "Принудительная многофакторная аутентификация (MFA)", "FORCEMFA": "Принудительная многофакторная аутентификация для всех пользователей",
"FORCEMFALOCALONLY": "Принудительная многофакторная аутентификация только для локально аутентифицированных пользователей",
"FORCEMFA_DESC": "Если данный параметр выбран, пользователи должны настроить двухфакторную аутентификацию для входа в систему.", "FORCEMFA_DESC": "Если данный параметр выбран, пользователи должны настроить двухфакторную аутентификацию для входа в систему.",
"HIDEPASSWORDRESET": "Скрыть сброс пароля", "HIDEPASSWORDRESET": "Скрыть сброс пароля",
"HIDEPASSWORDRESET_DESC": "Если данный параметр выбран, пользователь не может сбросить свой пароль в процессе входа в систему.", "HIDEPASSWORDRESET_DESC": "Если данный параметр выбран, пользователь не может сбросить свой пароль в процессе входа в систему.",
@ -2670,7 +2673,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Добавить менеджера", "ADD": "Добавить менеджера",

View File

@ -1388,7 +1388,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1625,7 +1626,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "E-postverifiering klar", "emailVerificationDoneText": "E-postverifiering klar",
@ -1725,8 +1727,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Den konventionella inloggningen med användarnamn och lösenord är tillåten.", "ALLOWUSERNAMEPASSWORD_DESC": "Den konventionella inloggningen med användarnamn och lösenord är tillåten.",
"ALLOWEXTERNALIDP_DESC": "Inloggning är tillåten för de underliggande identitetsleverantörerna", "ALLOWEXTERNALIDP_DESC": "Inloggning är tillåten för de underliggande identitetsleverantörerna",
"ALLOWREGISTER_DESC": "Om alternativet är valt visas ett ytterligare steg för att registrera en användare i inloggningen.", "ALLOWREGISTER_DESC": "Om alternativet är valt visas ett ytterligare steg för att registrera en användare i inloggningen.",
"FORCEMFA": "Tvinga MFA", "FORCEMFA": "Tvinga MFA för alla användare",
"FORCEMFALOCALONLY": "Tvinga MFA för lokalt autentiserade användare", "FORCEMFALOCALONLY": "Tvinga MFA endast för lokalt autentiserade användare",
"FORCEMFALOCALONLY_DESC": "Om alternativet är valt måste lokalt autentiserade användare konfigurera en andra faktor för inloggning.", "FORCEMFALOCALONLY_DESC": "Om alternativet är valt måste lokalt autentiserade användare konfigurera en andra faktor för inloggning.",
"HIDEPASSWORDRESET_DESC": "Om alternativet är valt kan användaren inte återställa sitt lösenord i inloggningsprocessen.", "HIDEPASSWORDRESET_DESC": "Om alternativet är valt kan användaren inte återställa sitt lösenord i inloggningsprocessen.",
"HIDELOGINNAMESUFFIX": "Dölj inloggningsnamn suffix", "HIDELOGINNAMESUFFIX": "Dölj inloggningsnamn suffix",
@ -2592,7 +2594,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "Lägg till en administratör", "ADD": "Lägg till en administratör",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia", "id": "Bahasa Indonesia",
"hu": "Magyar" "hu": "Magyar",
"ko": "한국어"
} }
}, },
"SMTP": { "SMTP": {
@ -1620,7 +1621,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"KEYS": { "KEYS": {
"emailVerificationDoneText": "电子邮件验证完成", "emailVerificationDoneText": "电子邮件验证完成",
@ -1720,8 +1722,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "允许使用用户名和密码进行登录。", "ALLOWUSERNAMEPASSWORD_DESC": "允许使用用户名和密码进行登录。",
"ALLOWEXTERNALIDP_DESC": "允许外部身份提供者进行登录", "ALLOWEXTERNALIDP_DESC": "允许外部身份提供者进行登录",
"ALLOWREGISTER_DESC": "如果选择了该选项,登录中会出现一个用于注册用户的附加步骤。", "ALLOWREGISTER_DESC": "如果选择了该选项,登录中会出现一个用于注册用户的附加步骤。",
"FORCEMFA": "强制使用 MFA", "FORCEMFA": "强制所有用户使用 MFA",
"FORCEMFALOCALONLY": "对本地用户强制执行 MFA", "FORCEMFALOCALONLY": "仅强制本地认证用户使用 MFA",
"FORCEMFALOCALONLY_DESC": "如果选择该选项,本地经过身份验证的用户必须配置第二个登录因素。", "FORCEMFALOCALONLY_DESC": "如果选择该选项,本地经过身份验证的用户必须配置第二个登录因素。",
"HIDEPASSWORDRESET_DESC": "如果选择该选项,则用户无法在登录过程中重置其密码。", "HIDEPASSWORDRESET_DESC": "如果选择该选项,则用户无法在登录过程中重置其密码。",
"HIDELOGINNAMESUFFIX": "隐藏登录名后缀", "HIDELOGINNAMESUFFIX": "隐藏登录名后缀",
@ -2563,7 +2565,8 @@
"ru": "Русский", "ru": "Русский",
"nl": "Nederlands", "nl": "Nederlands",
"sv": "Svenska", "sv": "Svenska",
"id": "Bahasa Indonesia" "id": "Bahasa Indonesia",
"ko": "한국어"
}, },
"MEMBER": { "MEMBER": {
"ADD": "添加管理者", "ADD": "添加管理者",

View File

@ -51,6 +51,7 @@ ZITADEL is available in the following languages
- Dutch (nl) - Dutch (nl)
- Swedish (sv) - Swedish (sv)
- Hungarian (hu) - Hungarian (hu)
- 한국어 (ko)
A language is displayed based on your agent's language header. A language is displayed based on your agent's language header.
If a users language header doesn't match any of the supported or [restricted](#restrict-languages) languages, the instances default language will be used. If a users language header doesn't match any of the supported or [restricted](#restrict-languages) languages, the instances default language will be used.

View File

@ -469,12 +469,12 @@ func updateAppleProviderToCommand(req *admin_pb.UpdateAppleProviderRequest) comm
} }
} }
func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) command.SAMLProvider { func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) *command.SAMLProvider {
var nameIDFormat *domain.SAMLNameIDFormat var nameIDFormat *domain.SAMLNameIDFormat
if req.NameIdFormat != nil { if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
} }
return command.SAMLProvider{ return &command.SAMLProvider{
Name: req.Name, Name: req.Name,
Metadata: req.GetMetadataXml(), Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(), MetadataURL: req.GetMetadataUrl(),
@ -486,12 +486,12 @@ func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) command.SAML
} }
} }
func updateSAMLProviderToCommand(req *admin_pb.UpdateSAMLProviderRequest) command.SAMLProvider { func updateSAMLProviderToCommand(req *admin_pb.UpdateSAMLProviderRequest) *command.SAMLProvider {
var nameIDFormat *domain.SAMLNameIDFormat var nameIDFormat *domain.SAMLNameIDFormat
if req.NameIdFormat != nil { if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
} }
return command.SAMLProvider{ return &command.SAMLProvider{
Name: req.Name, Name: req.Name,
Metadata: req.GetMetadataXml(), Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(), MetadataURL: req.GetMetadataUrl(),

View File

@ -462,12 +462,12 @@ func updateAppleProviderToCommand(req *mgmt_pb.UpdateAppleProviderRequest) comma
} }
} }
func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) command.SAMLProvider { func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) *command.SAMLProvider {
var nameIDFormat *domain.SAMLNameIDFormat var nameIDFormat *domain.SAMLNameIDFormat
if req.NameIdFormat != nil { if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
} }
return command.SAMLProvider{ return &command.SAMLProvider{
Name: req.Name, Name: req.Name,
Metadata: req.GetMetadataXml(), Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(), MetadataURL: req.GetMetadataUrl(),
@ -479,12 +479,12 @@ func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) command.SAMLP
} }
} }
func updateSAMLProviderToCommand(req *mgmt_pb.UpdateSAMLProviderRequest) command.SAMLProvider { func updateSAMLProviderToCommand(req *mgmt_pb.UpdateSAMLProviderRequest) *command.SAMLProvider {
var nameIDFormat *domain.SAMLNameIDFormat var nameIDFormat *domain.SAMLNameIDFormat
if req.NameIdFormat != nil { if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
} }
return command.SAMLProvider{ return &command.SAMLProvider{
Name: req.Name, Name: req.Name,
Metadata: req.GetMetadataXml(), Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(), MetadataURL: req.GetMetadataUrl(),

View File

@ -58,7 +58,7 @@ func TestServer_AddOTPSMS(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "user mismatch", name: "no permission",
args: args{ args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser), ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPSMSRequest{ req: &user.AddOTPSMSRequest{
@ -127,14 +127,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX) userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId()) Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId()) _, err := Instance.Client.UserV2.VerifyPhone(CTX, &user.VerifyPhoneRequest{
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Instance.Client.UserV2.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetPhoneCode(), VerificationCode: userVerified.GetPhoneCode(),
}) })
require.NoError(t, err) require.NoError(t, err)
_, err = Instance.Client.UserV2.AddOTPSMS(userVerifiedCtx, &user.AddOTPSMSRequest{UserId: userVerified.GetUserId()}) _, err = Instance.Client.UserV2.AddOTPSMS(CTX, &user.AddOTPSMSRequest{UserId: userVerified.GetUserId()})
require.NoError(t, err)
userSelf := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userSelf.GetUserId())
_, sessionTokenSelf, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userSelf.GetUserId())
userSelfCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenSelf)
_, err = Instance.Client.UserV2.VerifyPhone(CTX, &user.VerifyPhoneRequest{
UserId: userSelf.GetUserId(),
VerificationCode: userSelf.GetPhoneCode(),
})
require.NoError(t, err)
_, err = Instance.Client.UserV2.AddOTPSMS(CTX, &user.AddOTPSMSRequest{UserId: userSelf.GetUserId()})
require.NoError(t, err) require.NoError(t, err)
type args struct { type args struct {
@ -157,10 +167,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "success, self",
args: args{
ctx: userSelfCtx,
req: &user.RemoveOTPSMSRequest{
UserId: userSelf.GetUserId(),
},
},
want: &user.RemoveOTPSMSResponse{
Details: &object.Details{
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
},
},
},
{ {
name: "success", name: "success",
args: args{ args: args{
ctx: userVerifiedCtx, ctx: CTX,
req: &user.RemoveOTPSMSRequest{ req: &user.RemoveOTPSMSRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
}, },
@ -230,7 +254,7 @@ func TestServer_AddOTPEmail(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "user mismatch", name: "no permission",
args: args{ args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser), ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPEmailRequest{ req: &user.AddOTPEmailRequest{
@ -301,14 +325,24 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX) userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId()) Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId()) _, err := Instance.Client.UserV2.VerifyEmail(CTX, &user.VerifyEmailRequest{
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Instance.Client.UserV2.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetEmailCode(), VerificationCode: userVerified.GetEmailCode(),
}) })
require.NoError(t, err) require.NoError(t, err)
_, err = Instance.Client.UserV2.AddOTPEmail(userVerifiedCtx, &user.AddOTPEmailRequest{UserId: userVerified.GetUserId()}) _, err = Instance.Client.UserV2.AddOTPEmail(CTX, &user.AddOTPEmailRequest{UserId: userVerified.GetUserId()})
require.NoError(t, err)
userSelf := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userSelf.GetUserId())
_, sessionTokenSelf, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userSelf.GetUserId())
userSelfCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenSelf)
_, err = Instance.Client.UserV2.VerifyEmail(CTX, &user.VerifyEmailRequest{
UserId: userSelf.GetUserId(),
VerificationCode: userSelf.GetEmailCode(),
})
require.NoError(t, err)
_, err = Instance.Client.UserV2.AddOTPEmail(CTX, &user.AddOTPEmailRequest{UserId: userSelf.GetUserId()})
require.NoError(t, err) require.NoError(t, err)
type args struct { type args struct {
@ -331,10 +365,25 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "success, self",
args: args{
ctx: userSelfCtx,
req: &user.RemoveOTPEmailRequest{
UserId: userSelf.GetUserId(),
},
},
want: &user.RemoveOTPEmailResponse{
Details: &object.Details{
ChangeDate: timestamppb.Now(),
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
},
},
},
{ {
name: "success", name: "success",
args: args{ args: args{
ctx: userVerifiedCtx, ctx: CTX,
req: &user.RemoveOTPEmailRequest{ req: &user.RemoveOTPEmailRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
}, },

View File

@ -93,15 +93,30 @@ func TestServer_RegisterPasskey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "user mismatch", name: "user no permission",
args: args{ args: args{
ctx: CTX, ctx: UserCTX,
req: &user.RegisterPasskeyRequest{ req: &user.RegisterPasskeyRequest{
UserId: userID, UserId: userID,
}, },
}, },
wantErr: true, wantErr: true,
}, },
{
name: "user permission",
args: args{
ctx: IamCTX,
req: &user.RegisterPasskeyRequest{
UserId: userID,
},
},
want: &user.RegisterPasskeyResponse{
Details: &object.Details{
ChangeDate: timestamppb.Now(),
ResourceOwner: Instance.DefaultOrg.Id,
},
},
},
{ {
name: "user setting its own passkey", name: "user setting its own passkey",
args: args{ args: args{

View File

@ -13,7 +13,6 @@ func (s *Server) AddOTPSMS(ctx context.Context, req *user.AddOTPSMSRequest) (*us
return nil, err return nil, err
} }
return &user.AddOTPSMSResponse{Details: object.DomainToDetailsPb(details)}, nil return &user.AddOTPSMSResponse{Details: object.DomainToDetailsPb(details)}, nil
} }
func (s *Server) RemoveOTPSMS(ctx context.Context, req *user.RemoveOTPSMSRequest) (*user.RemoveOTPSMSResponse, error) { func (s *Server) RemoveOTPSMS(ctx context.Context, req *user.RemoveOTPSMSRequest) (*user.RemoveOTPSMSResponse, error) {

View File

@ -58,7 +58,7 @@ func TestServer_AddOTPSMS(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "user mismatch", name: "no permission",
args: args{ args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser), ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPSMSRequest{ req: &user.AddOTPSMSRequest{
@ -127,14 +127,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX) userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId()) Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId()) _, err := Instance.Client.UserV2beta.VerifyPhone(CTX, &user.VerifyPhoneRequest{
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Client.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetPhoneCode(), VerificationCode: userVerified.GetPhoneCode(),
}) })
require.NoError(t, err) require.NoError(t, err)
_, err = Client.AddOTPSMS(userVerifiedCtx, &user.AddOTPSMSRequest{UserId: userVerified.GetUserId()}) _, err = Instance.Client.UserV2beta.AddOTPSMS(CTX, &user.AddOTPSMSRequest{UserId: userVerified.GetUserId()})
require.NoError(t, err)
userSelf := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userSelf.GetUserId())
_, sessionTokenSelf, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userSelf.GetUserId())
userSelfCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenSelf)
_, err = Instance.Client.UserV2beta.VerifyPhone(CTX, &user.VerifyPhoneRequest{
UserId: userSelf.GetUserId(),
VerificationCode: userSelf.GetPhoneCode(),
})
require.NoError(t, err)
_, err = Instance.Client.UserV2beta.AddOTPSMS(CTX, &user.AddOTPSMSRequest{UserId: userSelf.GetUserId()})
require.NoError(t, err) require.NoError(t, err)
type args struct { type args struct {
@ -157,10 +167,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "success, self",
args: args{
ctx: userSelfCtx,
req: &user.RemoveOTPSMSRequest{
UserId: userSelf.GetUserId(),
},
},
want: &user.RemoveOTPSMSResponse{
Details: &object.Details{
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
},
},
},
{ {
name: "success", name: "success",
args: args{ args: args{
ctx: userVerifiedCtx, ctx: CTX,
req: &user.RemoveOTPSMSRequest{ req: &user.RemoveOTPSMSRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
}, },
@ -301,14 +325,24 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX) userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId()) Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId()) _, err := Client.VerifyEmail(CTX, &user.VerifyEmailRequest{
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Client.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetEmailCode(), VerificationCode: userVerified.GetEmailCode(),
}) })
require.NoError(t, err) require.NoError(t, err)
_, err = Client.AddOTPEmail(userVerifiedCtx, &user.AddOTPEmailRequest{UserId: userVerified.GetUserId()}) _, err = Client.AddOTPEmail(CTX, &user.AddOTPEmailRequest{UserId: userVerified.GetUserId()})
require.NoError(t, err)
userSelf := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userSelf.GetUserId())
_, sessionTokenSelf, _, _ := Instance.CreateVerifiedWebAuthNSession(t, IamCTX, userSelf.GetUserId())
userSelfCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenSelf)
_, err = Client.VerifyEmail(CTX, &user.VerifyEmailRequest{
UserId: userSelf.GetUserId(),
VerificationCode: userSelf.GetEmailCode(),
})
require.NoError(t, err)
_, err = Client.AddOTPEmail(CTX, &user.AddOTPEmailRequest{UserId: userSelf.GetUserId()})
require.NoError(t, err) require.NoError(t, err)
type args struct { type args struct {
@ -331,10 +365,25 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "success, self",
args: args{
ctx: userSelfCtx,
req: &user.RemoveOTPEmailRequest{
UserId: userSelf.GetUserId(),
},
},
want: &user.RemoveOTPEmailResponse{
Details: &object.Details{
ChangeDate: timestamppb.Now(),
ResourceOwner: Instance.DefaultOrg.Details.ResourceOwner,
},
},
},
{ {
name: "success", name: "success",
args: args{ args: args{
ctx: userVerifiedCtx, ctx: CTX,
req: &user.RemoveOTPEmailRequest{ req: &user.RemoveOTPEmailRequest{
UserId: userVerified.GetUserId(), UserId: userVerified.GetUserId(),
}, },

View File

@ -92,15 +92,30 @@ func TestServer_RegisterPasskey(t *testing.T) {
wantErr: true, wantErr: true,
}, },
{ {
name: "user mismatch", name: "user no permission",
args: args{ args: args{
ctx: CTX, ctx: UserCTX,
req: &user.RegisterPasskeyRequest{ req: &user.RegisterPasskeyRequest{
UserId: userID, UserId: userID,
}, },
}, },
wantErr: true, wantErr: true,
}, },
{
name: "user permission",
args: args{
ctx: IamCTX,
req: &user.RegisterPasskeyRequest{
UserId: userID,
},
},
want: &user.RegisterPasskeyResponse{
Details: &object.Details{
ChangeDate: timestamppb.Now(),
ResourceOwner: Instance.DefaultOrg.Id,
},
},
},
{ {
name: "user setting its own passkey", name: "user setting its own passkey",
args: args{ args: args{

View File

@ -260,6 +260,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Пол GenderLabel: Пол
Female: Женски пол Female: Женски пол
Male: Мъжки Male: Мъжки
@ -301,6 +302,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Правила и условия TosAndPrivacyLabel: Правила и условия
TosConfirm: Приемам TosConfirm: Приемам
TosLinkText: TOS TosLinkText: TOS
@ -371,6 +373,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Упълномощаване на устройството Title: Упълномощаване на устройството
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Pohlaví GenderLabel: Pohlaví
Female: Žena Female: Žena
Male: Muž Male: Muž
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Obchodní podmínky TosAndPrivacyLabel: Obchodní podmínky
TosConfirm: Souhlasím s TosConfirm: Souhlasím s
TosLinkText: obchodními podmínkami TosLinkText: obchodními podmínkami
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Autorizace zařízení Title: Autorizace zařízení
UserCode: UserCode:

View File

@ -263,6 +263,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Geschlecht GenderLabel: Geschlecht
Female: weiblich Female: weiblich
Male: männlich Male: männlich
@ -305,6 +306,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz
TosConfirm: Ich akzeptiere die TosConfirm: Ich akzeptiere die
TosLinkText: AGB TosLinkText: AGB
@ -381,6 +383,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Gerät verbinden Title: Gerät verbinden
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Gender GenderLabel: Gender
Female: Female Female: Female
Male: Male Male: Male
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Terms and conditions TosAndPrivacyLabel: Terms and conditions
TosConfirm: I accept the TosConfirm: I accept the
TosLinkText: TOS TosLinkText: TOS
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Device Authorization Title: Device Authorization
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Género GenderLabel: Género
Female: Mujer Female: Mujer
Male: Hombre Male: Hombre
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Términos y condiciones TosAndPrivacyLabel: Términos y condiciones
TosConfirm: Acepto los TosConfirm: Acepto los
TosLinkText: TDS TosLinkText: TDS
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
Footer: Footer:
PoweredBy: Powered By PoweredBy: Powered By

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Genre GenderLabel: Genre
Female: Femme Female: Femme
Male: Homme Male: Homme
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Termes et conditions TosAndPrivacyLabel: Termes et conditions
TosConfirm: J'accepte les TosConfirm: J'accepte les
TosLinkText: TOS TosLinkText: TOS
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Autorisation de l'appareil Title: Autorisation de l'appareil

View File

@ -234,6 +234,7 @@ RegistrationUser:
Swedish: Svéd Swedish: Svéd
Indonesian: Indonéz Indonesian: Indonéz
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Nem GenderLabel: Nem
Female: Female:
Male: Férfi Male: Férfi
@ -275,6 +276,7 @@ ExternalRegistrationUserOverview:
Swedish: Svéd Swedish: Svéd
Indonesian: Indonéz Indonesian: Indonéz
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Felhasználási feltételek TosAndPrivacyLabel: Felhasználási feltételek
TosConfirm: Elfogadom a TosConfirm: Elfogadom a
TosLinkText: TOS TosLinkText: TOS
@ -345,6 +347,7 @@ ExternalNotFound:
Swedish: Svéd Swedish: Svéd
Indonesian: Indonéz Indonesian: Indonéz
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Eszköz engedélyezése Title: Eszköz engedélyezése
UserCode: UserCode:

View File

@ -234,6 +234,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Jenis kelamin GenderLabel: Jenis kelamin
Female: Perempuan Female: Perempuan
Male: Pria Male: Pria
@ -274,6 +275,7 @@ ExternalRegistrationUserOverview:
Dutch: Nederlands Dutch: Nederlands
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Korean: 한국어
TosAndPrivacyLabel: Syarat dan Ketentuan TosAndPrivacyLabel: Syarat dan Ketentuan
TosConfirm: Saya menerima itu TosConfirm: Saya menerima itu
TosLinkText: KL TosLinkText: KL
@ -344,6 +346,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Otorisasi Perangkat Title: Otorisasi Perangkat
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Genere GenderLabel: Genere
Female: Femminile Female: Femminile
Male: Maschile Male: Maschile
@ -305,6 +306,7 @@ ExternalRegistrationUserOverview:
Dutch: Nederlands Dutch: Nederlands
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Korean: 한국어
TosAndPrivacyLabel: Termini di servizio TosAndPrivacyLabel: Termini di servizio
TosConfirm: Accetto i TosConfirm: Accetto i
TosLinkText: Termini di servizio TosLinkText: Termini di servizio
@ -381,6 +383,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Autorizzazione del dispositivo Title: Autorizzazione del dispositivo

View File

@ -256,6 +256,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: 性別 GenderLabel: 性別
Female: 女性 Female: 女性
Male: 男性 Male: 男性
@ -298,6 +299,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: 利用規約 TosAndPrivacyLabel: 利用規約
TosConfirm: 私は利用規約を承諾します。 TosConfirm: 私は利用規約を承諾します。
TosLinkText: TOS TosLinkText: TOS
@ -374,6 +376,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: デバイス認証 Title: デバイス認証

View File

@ -0,0 +1,521 @@
Login:
Title: 다시 오신 것을 환영합니다!
Description: 로그인 정보를 입력하세요.
TitleLinking: 사용자 연결을 위한 로그인
DescriptionLinking: 외부 사용자를 연결하려면 로그인 정보를 입력하세요.
LoginNameLabel: 로그인 이름
UsernamePlaceHolder: 사용자 이름
LoginnamePlaceHolder: username@domain
ExternalUserDescription: 외부 사용자로 로그인하세요.
MustBeMemberOfOrg: 사용자는 {{.OrgName}} 조직의 멤버여야 합니다.
RegisterButtonText: 등록
NextButtonText: 다음
LDAP:
Title: 로그인
Description: 로그인 정보를 입력하세요.
LoginNameLabel: 로그인 이름
PasswordLabel: 비밀번호
NextButtonText: 다음
SelectAccount:
Title: 계정 선택
Description: 계정을 사용하세요
TitleLinking: 사용자 연결을 위한 계정 선택
DescriptionLinking: 외부 사용자와 연결할 계정을 선택하세요.
OtherUser: 다른 사용자
SessionState0: 활성
SessionState1: 로그아웃됨
MustBeMemberOfOrg: 사용자는 {{.OrgName}} 조직의 멤버여야 합니다.
Password:
Title: 비밀번호
Description: 로그인 정보를 입력하세요.
PasswordLabel: 비밀번호
MinLength: 최소 길이는
MinLengthp2: 자 이상이어야 합니다.
MaxLength: 70자 미만이어야 합니다.
HasUppercase: 대문자가 포함되어야 합니다.
HasLowercase: 소문자가 포함되어야 합니다.
HasNumber: 숫자가 포함되어야 합니다.
HasSymbol: 기호가 포함되어야 합니다.
Confirmation: 비밀번호가 일치합니다.
ResetLinkText: 비밀번호 재설정
BackButtonText: 뒤로
NextButtonText: 다음
UsernameChange:
Title: 사용자 이름 변경
Description: 새 사용자 이름을 설정하세요
UsernameLabel: 사용자 이름
CancelButtonText: 취소
NextButtonText: 다음
UsernameChangeDone:
Title: 사용자 이름 변경 완료
Description: 사용자 이름이 성공적으로 변경되었습니다.
NextButtonText: 다음
InitPassword:
Title: 비밀번호 설정
Description: 아래 양식에 새 비밀번호를 설정하기 위해 받은 코드를 입력하세요.
CodeLabel: 코드
NewPasswordLabel: 새 비밀번호
NewPasswordConfirmLabel: 비밀번호 확인
ResendButtonText: 코드 재전송
NextButtonText: 다음
InitPasswordDone:
Title: 비밀번호 설정 완료
Description: 비밀번호가 성공적으로 설정되었습니다.
NextButtonText: 다음
CancelButtonText: 취소
InitUser:
Title: 사용자 활성화
Description: 아래 코드를 통해 이메일을 인증하고 비밀번호를 설정하세요.
CodeLabel: 코드
NewPasswordLabel: 새 비밀번호
NewPasswordConfirm: 비밀번호 확인
NextButtonText: 다음
ResendButtonText: 코드 재전송
InitUserDone:
Title: 사용자 활성화 완료
Description: 이메일이 인증되었으며 비밀번호가 성공적으로 설정되었습니다.
NextButtonText: 다음
CancelButtonText: 취소
InviteUser:
Title: 사용자 활성화
Description: 아래 코드를 통해 이메일을 인증하고 비밀번호를 설정하세요.
CodeLabel: 코드
NewPasswordLabel: 새 비밀번호
NewPasswordConfirm: 비밀번호 확인
NextButtonText: 다음
ResendButtonText: 코드 재전송
InitMFAPrompt:
Title: 2단계 인증 설정
Description: 2단계 인증은 사용자 계정에 추가 보안을 제공합니다. 이를 통해 계정 접근이 본인에게만 허용됩니다.
Provider0: "인증 앱 (예: Google/Microsoft Authenticator, Authy)"
Provider1: "장치 종속 (예: FaceID, Windows Hello, 지문)"
Provider3: OTP SMS
Provider4: OTP 이메일
NextButtonText: 다음
SkipButtonText: 건너뛰기
InitMFAOTP:
Title: 2단계 인증
Description: 2단계 인증을 설정하세요. 인증 앱이 없으면 다운로드하세요.
OTPDescription: "인증 앱으로 코드를 스캔하거나 비밀을 복사하여 아래에 생성된 코드를 입력하세요 (예: Google/Microsoft Authenticator, Authy)."
SecretLabel: 비밀
CodeLabel: 코드
NextButtonText: 다음
CancelButtonText: 취소
InitMFAOTPSMS:
Title: 2단계 인증
DescriptionPhone: 2단계 인증을 설정하세요. 전화번호를 입력하여 인증하세요.
DescriptionCode: 2단계 인증을 설정하세요. 받은 코드를 입력하여 전화번호를 인증하세요.
PhoneLabel: 전화번호
CodeLabel: 코드
EditButtonText: 수정
ResendButtonText: 코드 재전송
NextButtonText: 다음
InitMFAU2F:
Title: 보안 키 추가
Description: 보안 키는 휴대폰에 내장되거나, 블루투스 또는 컴퓨터 USB 포트에 직접 연결할 수 있는 인증 방법입니다.
TokenNameLabel: 보안 키/장치 이름
NotSupported: "WebAuthN이 브라우저에서 지원되지 않습니다. 최신 상태인지 확인하거나 다른 브라우저를 사용하세요 (예: Chrome, Safari, Firefox)."
RegisterTokenButtonText: 보안 키 추가
ErrorRetry: 다시 시도, 새 챌린지 생성 또는 다른 방법 선택.
InitMFADone:
Title: 2단계 인증 완료
Description: 축하합니다! 2단계 인증을 성공적으로 설정하여 계정을 더욱 안전하게 보호했습니다. 로그인 시마다 이 인증이 필요합니다.
NextButtonText: 다음
CancelButtonText: 취소
MFAProvider:
Provider0: "인증 앱 (예: Google/Microsoft Authenticator, Authy)"
Provider1: "장치 종속 (예: FaceID, Windows Hello, 지문)"
Provider3: OTP SMS
Provider4: OTP 이메일
ChooseOther: 다른 옵션 선택
VerifyMFAOTP:
Title: 2단계 인증 확인
Description: 2단계 인증을 확인하세요
CodeLabel: 코드
NextButtonText: 다음
VerifyOTP:
Title: 2단계 인증 확인
Description: 2단계 인증을 확인하세요
CodeLabel: 코드
ResendButtonText: 코드 재전송
NextButtonText: 다음
VerifyMFAU2F:
Title: 2단계 인증
Description: "등록된 장치로 2단계 인증을 진행하세요 (예: FaceID, Windows Hello, 지문)"
NotSupported: "WebAuthN이 브라우저에서 지원되지 않습니다. 최신 버전을 사용하거나 지원되는 다른 브라우저로 변경하세요 (예: Chrome, Safari, Firefox)."
ErrorRetry: 다시 시도, 새 요청 생성 또는 다른 방법 선택.
ValidateTokenButtonText: 2단계 인증
Passwordless:
Title: 비밀번호 없이 로그인
Description: "FaceID, Windows Hello, 지문과 같은 장치에서 제공하는 인증 방법으로 로그인하세요."
NotSupported: "WebAuthN이 브라우저에서 지원되지 않습니다. 최신 상태인지 확인하거나 다른 브라우저를 사용하세요 (예: Chrome, Safari, Firefox)."
ErrorRetry: 다시 시도, 새 챌린지 생성 또는 다른 방법 선택.
LoginWithPwButtonText: 비밀번호로 로그인
ValidateTokenButtonText: 비밀번호 없이 로그인
PasswordlessPrompt:
Title: 비밀번호 없는 로그인 설정
Description: "비밀번호 없는 로그인을 설정하시겠습니까? (FaceID, Windows Hello, 지문과 같은 장치 인증 방법)"
DescriptionInit: 비밀번호 없는 로그인을 설정해야 합니다. 기기 등록을 위해 제공된 링크를 사용하세요.
PasswordlessButtonText: 비밀번호 없이 사용
NextButtonText: 다음
SkipButtonText: 건너뛰기
PasswordlessRegistration:
Title: 비밀번호 없는 로그인 설정
Description: "장치 이름을 입력한 후 아래의 '비밀번호 없이 등록' 버튼을 클릭하여 인증을 추가하세요 (예: 내 휴대폰, MacBook 등)."
TokenNameLabel: 장치 이름
NotSupported: "WebAuthN이 브라우저에서 지원되지 않습니다. 최신 상태인지 확인하거나 다른 브라우저를 사용하세요 (예: Chrome, Safari, Firefox)."
RegisterTokenButtonText: 비밀번호 없이 등록
ErrorRetry: 다시 시도, 새 챌린지 생성 또는 다른 방법 선택.
PasswordlessRegistrationDone:
Title: 비밀번호 없는 로그인 설정 완료
Description: 비밀번호 없는 장치가 성공적으로 추가되었습니다.
DescriptionClose: 이제 이 창을 닫을 수 있습니다.
NextButtonText: 다음
CancelButtonText: 취소
PasswordChange:
Title: 비밀번호 변경
Description: 비밀번호를 변경하세요. 기존 비밀번호와 새 비밀번호를 입력하세요.
ExpiredDescription: 비밀번호가 만료되어 변경이 필요합니다. 기존 비밀번호와 새 비밀번호를 입력하세요.
OldPasswordLabel: 기존 비밀번호
NewPasswordLabel: 새 비밀번호
NewPasswordConfirmLabel: 비밀번호 확인
CancelButtonText: 취소
NextButtonText: 다음
Footer: 푸터
PasswordChangeDone:
Title: 비밀번호 변경 완료
Description: 비밀번호가 성공적으로 변경되었습니다.
NextButtonText: 다음
PasswordResetDone:
Title: 비밀번호 재설정 링크 발송됨
Description: 비밀번호를 재설정하려면 이메일을 확인하세요.
NextButtonText: 다음
EmailVerification:
Title: 이메일 인증
Description: 이메일 인증을 위해 전송된 코드를 아래 양식에 입력하세요.
CodeLabel: 코드
NextButtonText: 다음
ResendButtonText: 코드 재전송
EmailVerificationDone:
Title: 이메일 인증 완료
Description: 이메일 주소가 성공적으로 인증되었습니다.
NextButtonText: 다음
CancelButtonText: 취소
LoginButtonText: 로그인
RegisterOption:
Title: 등록 옵션
Description: 등록 방법을 선택하세요
RegisterUsernamePasswordButtonText: 사용자 이름과 비밀번호로 등록
ExternalLoginDescription: 또는 외부 사용자로 등록
LoginButtonText: 로그인
RegistrationUser:
Title: 등록
Description: 사용자 정보를 입력하세요. 이메일 주소는 로그인 이름으로 사용됩니다.
DescriptionOrgRegister: 사용자 정보를 입력하세요.
EmailLabel: 이메일
UsernameLabel: 사용자 이름
FirstnameLabel: 이름
LastnameLabel:
LanguageLabel: 언어
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
GenderLabel: 성별
Female: 여성
Male: 남성
Diverse: 기타 / X
PasswordLabel: 비밀번호
PasswordConfirmLabel: 비밀번호 확인
TosAndPrivacyLabel: 동의사항
TosConfirm: 이용 약관에 동의합니다.
TosLinkText: 이용 약관
PrivacyConfirm: 개인정보 수집 및 이용에 동의합니다.
PrivacyLinkText: 개인정보처리방침
ExternalLogin: 또는 외부 사용자로 등록
BackButtonText: 로그인
NextButtonText: 다음
ExternalRegistrationUserOverview:
Title: 외부 사용자 등록
Description: 선택한 제공자에서 사용자 정보를 가져왔습니다. 이제 정보를 수정하거나 완성할 수 있습니다.
EmailLabel: 이메일
UsernameLabel: 사용자 이름
FirstnameLabel: 이름
LastnameLabel:
NicknameLabel: 닉네임
PhoneLabel: 전화번호
LanguageLabel: 언어
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: 동의사항
TosConfirm: 이용 약관에 동의합니다.
TosLinkText: 이용 약관
PrivacyConfirm: 개인정보 수집 및 이용에 동의합니다.
PrivacyLinkText: 개인정보처리방침
ExternalLogin: 또는 외부 사용자로 등록
BackButtonText: 뒤로
NextButtonText: 저장
RegistrationOrg:
Title: 조직 등록
Description: 조직 이름과 사용자 정보를 입력하세요.
OrgNameLabel: 조직 이름
EmailLabel: 이메일
UsernameLabel: 사용자 이름
FirstnameLabel: 이름
LastnameLabel:
PasswordLabel: 비밀번호
PasswordConfirmLabel: 비밀번호 확인
TosAndPrivacyLabel: 동의사항
TosConfirm: 이용 약관에 동의합니다.
TosLinkText: 이용 약관
PrivacyConfirm: 개인정보 수집 및 이용에 동의합니다.
PrivacyLinkText: 개인정보처리방침
SaveButtonText: 조직 생성
LoginSuccess:
Title: 로그인 성공
AutoRedirectDescription: 자동으로 애플리케이션으로 리디렉션됩니다. 그렇지 않으면 아래 버튼을 클릭하세요. 이후 창을 닫아도 됩니다.
RedirectedDescription: 이제 이 창을 닫을 수 있습니다.
NextButtonText: 다음
LogoutDone:
Title: 로그아웃 완료
Description: 성공적으로 로그아웃되었습니다.
LoginButtonText: 로그인
LinkingUserPrompt:
Title: 기존 사용자 발견
Description: "기존 계정을 연결하시겠습니까:"
LinkButtonText: 연결
OtherButtonText: 다른 옵션
LinkingUsersDone:
Title: 사용자 연결
Description: 사용자가 연결되었습니다.
CancelButtonText: 취소
NextButtonText: 다음
ExternalNotFound:
Title: 외부 사용자 찾을 수 없음
Description: 외부 사용자를 찾을 수 없습니다. 사용자 계정을 연결하거나 새 계정을 자동 등록하시겠습니까?
LinkButtonText: 연결
AutoRegisterButtonText: 등록
TosAndPrivacyLabel: 동의사항
TosConfirm: 이용 약관에 동의합니다.
TosLinkText: 이용 약관
PrivacyConfirm: 개인정보 수집 및 이용에 동의합니다.
PrivacyLinkText: 개인정보처리방침
German: Deutsch
English: English
Italian: Italiano
French: Français
Chinese: 简体中文
Polish: Polski
Japanese: 日本語
Spanish: Español
Bulgarian: Български
Portuguese: Português
Macedonian: Македонски
Czech: Čeština
Russian: Русский
Dutch: Nederlands
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
DeviceAuth:
Title: 기기 인증
UserCode:
Label: 사용자 코드
Description: 기기에서 표시된 사용자 코드를 입력하세요.
ButtonNext: 다음
Action:
Description: 기기 접근 권한을 부여하세요.
GrantDevice: 기기에게 권한을 부여하려고 합니다
AccessToScopes: 다음 범위에 접근 권한이 있습니다
Button:
Allow: 허용
Deny: 거부
Done:
Description: 완료.
Approved: 기기 인증이 승인되었습니다. 이제 기기로 돌아가세요.
Denied: 기기 인증이 거부되었습니다. 이제 기기로 돌아가세요.
Footer:
PoweredBy: 제공자
Tos: 이용 약관
PrivacyPolicy: 개인정보처리방침
Help: 도움말
SupportEmail: 지원 이메일
SignIn: "{{.Provider}}로 로그인"
Errors:
Internal: 내부 오류가 발생했습니다
AuthRequest:
NotFound: 인증 요청을 찾을 수 없습니다
UserAgentNotCorresponding: 사용자 에이전트가 일치하지 않습니다
UserAgentNotFound: 사용자 에이전트 ID를 찾을 수 없습니다
TokenNotFound: 토큰을 찾을 수 없습니다
RequestTypeNotSupported: 요청 유형이 지원되지 않습니다
MissingParameters: 필수 매개변수가 없습니다
User:
NotFound: 사용자를 찾을 수 없습니다
AlreadyExists: 사용자가 이미 존재합니다
Inactive: 사용자가 비활성화되었습니다
NotFoundOnOrg: 선택된 조직에서 사용자를 찾을 수 없습니다
NotAllowedOrg: 사용자는 필수 조직의 멤버가 아닙니다
NotMatchingUserID: 사용자와 인증 요청의 사용자가 일치하지 않습니다
UserIDMissing: 사용자 ID가 비어 있습니다
Invalid: 잘못된 사용자 데이터입니다
DomainNotAllowedAsUsername: 도메인이 이미 예약되어 사용할 수 없습니다
NotAllowedToLink: 외부 로그인 제공자와 연결할 수 없습니다
Profile:
NotFound: 프로필을 찾을 수 없습니다
NotChanged: 프로필이 변경되지 않았습니다
Empty: 프로필이 비어 있습니다
FirstNameEmpty: 프로필에 이름이 비어 있습니다
LastNameEmpty: 프로필에 성이 비어 있습니다
IDMissing: 프로필 ID가 없습니다
Email:
NotFound: 이메일을 찾을 수 없습니다
Invalid: 잘못된 이메일입니다
AlreadyVerified: 이메일이 이미 인증되었습니다
NotChanged: 이메일이 변경되지 않았습니다
Empty: 이메일이 비어 있습니다
IDMissing: 이메일 ID가 없습니다
Phone:
NotFound: 전화번호를 찾을 수 없습니다
Invalid: 잘못된 전화번호입니다
AlreadyVerified: 전화번호가 이미 인증되었습니다
Empty: 전화번호가 비어 있습니다
NotChanged: 전화번호가 변경되지 않았습니다
Address:
NotFound: 주소를 찾을 수 없습니다
NotChanged: 주소가 변경되지 않았습니다
Username:
AlreadyExists: 사용자 이름이 이미 사용 중입니다
Reserved: 사용자 이름이 이미 예약되었습니다
Empty: 사용자 이름이 비어 있습니다
Password:
ConfirmationWrong: 비밀번호 확인이 일치하지 않습니다
Empty: 비밀번호가 비어 있습니다
Invalid: 잘못된 비밀번호입니다
InvalidAndLocked: 비밀번호가 잘못되었고 사용자가 잠겼습니다. 관리자에게 문의하세요.
NotChanged: 새 비밀번호는 현재 비밀번호와 다르게 설정해야 합니다
UsernameOrPassword:
Invalid: 사용자 이름 또는 비밀번호가 잘못되었습니다
PasswordComplexityPolicy:
NotFound: 비밀번호 정책을 찾을 수 없습니다
MinLength: 비밀번호가 너무 짧습니다
HasLower: 비밀번호에 소문자가 포함되어야 합니다
HasUpper: 비밀번호에 대문자가 포함되어야 합니다
HasNumber: 비밀번호에 숫자가 포함되어야 합니다
HasSymbol: 비밀번호에 기호가 포함되어야 합니다
Code:
Expired: 코드가 만료되었습니다
Invalid: 잘못된 코드입니다
Empty: 코드가 비어 있습니다
CryptoCodeNil: 암호화 코드가 없습니다
NotFound: 코드를 찾을 수 없습니다
GeneratorAlgNotSupported: 지원되지 않는 생성 알고리즘입니다
EmailVerify:
UserIDEmpty: 사용자 ID가 비어 있습니다
ExternalData:
CouldNotRead: 외부 데이터를 올바르게 읽을 수 없습니다
MFA:
NoProviders: 사용 가능한 다중 인증 제공자가 없습니다
OTP:
AlreadyReady: 다중 인증 OTP(일회용 비밀번호)가 이미 설정되었습니다
NotExisting: 다중 인증 OTP(일회용 비밀번호)가 존재하지 않습니다
InvalidCode: 잘못된 코드입니다
NotReady: 다중 인증 OTP(일회용 비밀번호)가 준비되지 않았습니다
Locked: 사용자가 잠겼습니다
SomethingWentWrong: 문제가 발생했습니다
NotActive: 사용자가 활성 상태가 아닙니다
ExternalIDP:
IDPTypeNotImplemented: IDP 유형이 구현되지 않았습니다
NotAllowed: 외부 로그인 제공자가 허용되지 않습니다
IDPConfigIDEmpty: ID 제공자 ID가 비어 있습니다
ExternalUserIDEmpty: 외부 사용자 ID가 비어 있습니다
UserDisplayNameEmpty: 사용자 표시 이름이 비어 있습니다
NoExternalUserData: 외부 사용자 데이터를 받을 수 없습니다
CreationNotAllowed: 이 제공자에서는 새 사용자 생성을 허용하지 않습니다
LinkingNotAllowed: 이 제공자에서는 사용자를 연결할 수 없습니다
NoOptionAllowed: 이 제공자에서는 생성과 연결이 모두 허용되지 않습니다. 관리자에게 문의하세요.
GrantRequired: 로그인 불가. 사용자는 애플리케이션에서 최소한 하나의 권한이 필요합니다. 관리자에게 문의하세요.
ProjectRequired: 로그인 불가. 사용자의 조직이 프로젝트에 허가되어야 합니다. 관리자에게 문의하세요.
IdentityProvider:
InvalidConfig: ID 제공자 설정이 잘못되었습니다
IAM:
LockoutPolicy:
NotExisting: 잠금 정책이 존재하지 않습니다
Org:
LoginPolicy:
RegistrationNotAllowed: 등록이 허용되지 않습니다
DeviceAuth:
NotExisting: 사용자 코드가 존재하지 않습니다
optional: (선택 사항)

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Пол GenderLabel: Пол
Female: Женски Female: Женски
Male: Машки Male: Машки
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Правила и услови TosAndPrivacyLabel: Правила и услови
TosConfirm: Се согласувам со TosConfirm: Се согласувам со
TosLinkText: правилата за користење TosLinkText: правилата за користење
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Овластување преку уред Title: Овластување преку уред

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Geslacht GenderLabel: Geslacht
Female: Vrouw Female: Vrouw
Male: Man Male: Man
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Algemene voorwaarden TosAndPrivacyLabel: Algemene voorwaarden
TosConfirm: Ik accepteer de TosConfirm: Ik accepteer de
TosLinkText: AV TosLinkText: AV
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Apparaat Autorisatie Title: Apparaat Autorisatie
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Płeć GenderLabel: Płeć
Female: Kobieta Female: Kobieta
Male: Mężczyzna Male: Mężczyzna
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Warunki i zasady TosAndPrivacyLabel: Warunki i zasady
TosConfirm: Akceptuję TosConfirm: Akceptuję
TosLinkText: Warunki korzystania TosLinkText: Warunki korzystania
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Autoryzacja urządzenia Title: Autoryzacja urządzenia

View File

@ -260,6 +260,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Gênero GenderLabel: Gênero
Female: Feminino Female: Feminino
Male: Masculino Male: Masculino
@ -302,6 +303,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Termos e condições TosAndPrivacyLabel: Termos e condições
TosConfirm: Eu aceito os TosConfirm: Eu aceito os
TosLinkText: termos de serviço TosLinkText: termos de serviço
@ -378,6 +380,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Autorização de dispositivo Title: Autorização de dispositivo

View File

@ -1,246 +1,247 @@
Login: Login:
Title: Добро пожаловать! Title: Добро пожаловать!
Description: Введите ваши данные. Description: Введите свои данные дял входа.
TitleLinking: Вход для привязки пользователей TitleLinking: Вход для привязки пользователей
DescriptionLinking: Введите данные для входа, чтобы привязать внешнего пользователя к пользователю ZITADEL. DescriptionLinking: Введите данные для входа, чтобы привязать внешнего пользователя к учётной записи ZITADEL.
LoginNameLabel: Логин LoginNameLabel: Логин
UsernamePlaceHolder: логин UsernamePlaceHolder: логин
LoginnamePlaceHolder: username@domain LoginnamePlaceHolder: username@domain
ExternalUserDescription: Войти под внешним пользователем. ExternalUserDescription: Войти как внешний пользователь.
MustBeMemberOfOrg: Пользователь должен быть участником организации {{.OrgName}}. MustBeMemberOfOrg: Пользователь должен быть членом организации {{.OrgName}}.
RegisterButtonText: зарегистрироваться RegisterButtonText: Зарегистрироваться
NextButtonText: далее NextButtonText: Продолжить
LDAP: LDAP:
Title: Войти Title: Войти
Description: Введите ваши данные дял входа. Description: Введите свои данные для входа.
LoginNameLabel: Имя пользователя LoginNameLabel: Логин
PasswordLabel: Пароль PasswordLabel: Пароль
NextButtonText: следующий NextButtonText: Продолжить
SelectAccount: SelectAccount:
Title: Выбор учётной записи Title: Выбор учётной записи
Description: Выберите вашу учётную запись. Description: Выберите учётную запись.
TitleLinking: Выберите учётную запись для привязки пользователя TitleLinking: Выберите учётную запись для привязки пользователя
DescriptionLinking: Выберите свою учётную запись для связи с внешним пользователем. DescriptionLinking: Выберите свою учётную запись для связи с внешним пользователем.
OtherUser: Другой пользователь OtherUser: Другая учётная запись
SessionState0: активный SessionState0: активный
SessionState1: неактивный SessionState1: неактивный
MustBeMemberOfOrg: Пользователь должен быть участником организации {{.OrgName}}. MustBeMemberOfOrg: Пользователь должен быть членом организации {{.OrgName}}.
Password: Password:
Title: Пароль Title: Пароль
Description: Введите свои данные для входа. Description: Введите данные для входа.
PasswordLabel: Пароль PasswordLabel: Пароль
MinLength: Должно быть не менее MinLength: Минимум
MinLengthp2: символов. MinLengthp2: символов.
MaxLength: Должно быть меньше 70 символов. MaxLength: Не более 70 символов.
HasUppercase: Должно содержать заглавную букву. HasUppercase: Должен содержать заглавную букву.
HasLowercase: Должно содержать строчную букву. HasLowercase: Должен содержать строчную букву.
HasNumber: Должно содержать число. HasNumber: Должен содержать цифру.
HasSymbol: Должно содержать символ. HasSymbol: Должен содержить специальный символ.
Confirmation: Подтверждение пароля совпадает. Confirmation: Пароли должны совпадать.
ResetLinkText: Сбросить пароль ResetLinkText: Сбросить пароль
BackButtonText: Назад BackButtonText: Назад
NextButtonText: Вперед NextButtonText: Продолжить
UsernameChange: UsernameChange:
Title: Изменение логина Title: Изменение логина
Description: Установите новый логин. Description: Установите новый логин.
UsernameLabel: Логин UsernameLabel: Логин
CancelButtonText: отмена CancelButtonText: Отмена
NextButtonText: далее NextButtonText: Продолжить
UsernameChangeDone: UsernameChangeDone:
Title: Логин изменён Title: Логин изменён
Description: Ваш логин был успешно изменён. Description: Ваш логин был успешно изменён.
NextButtonText: далее NextButtonText: Продолжить
InitPassword: InitPassword:
Title: Установка пароля Title: Установка пароля
Description: Введите код из письма, отправленного на вашу электронную почту, чтобы установить пароль. Description: Введите код из письма, отправленного на вашу электронную почту, чтобы установить пароль.
CodeLabel: Код CodeLabel: Код из письма
NewPasswordLabel: Новый пароль NewPasswordLabel: Новый пароль
NewPasswordConfirmLabel: Подтверждение пароля NewPasswordConfirmLabel: Повторите пароль
ResendButtonText: повторно отправить код ResendButtonText: Отправить код ещё раз
NextButtonText: далее NextButtonText: Продолжить
InitPasswordDone: InitPasswordDone:
Title: Пароль установлен Title: Пароль установлен
Description: Пароль успешно установлен. Description: Пароль успешно установлен.
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
InitUser: InitUser:
Title: Активация пользователя Title: Активация учётной записи
Description: Подтвердите вашу электронную почту кодом из письма и установите пароль. Description: Введите код из письма для подтверждения электронной почты и установите новый пароль.
CodeLabel: Код CodeLabel: Код из письма
NewPasswordLabel: Новый пароль NewPasswordLabel: Новый пароль
NewPasswordConfirm: Подтверждение пароля NewPasswordConfirm: Повторите пароль
NextButtonText: далее NextButtonText: Продолжить
ResendButtonText: повторно отправить код ResendButtonText: Отправить код ещё раз
InitUserDone: InitUserDone:
Title: Пользователь активирован Title: Учётная запись активирована
Description: Электронная почта подтверждена и пароль успешно установлен. Description: Электронная почта подтверждена и пароль успешно установлен.
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
InviteUser: InviteUser:
Title: Активировать пользователя Title: Активация учётной записи
Description: Проверьте свой адрес электронной почты с помощью кода ниже и установите свой пароль. Description: Введите код из письма для подтверждения электронной почты и установите новый пароль.
CodeLabel: Код CodeLabel: Код из письма
NewPasswordLabel: Новый пароль NewPasswordLabel: Новый пароль
NewPasswordConfirm: Подтвердить пароль NewPasswordConfirm: Повторите пароль
NextButtonText: Далее NextButtonText: Продолжить
ResendButtonText: Отправить код повторно ResendButtonText: Отправить код ещё раз
InitMFAPrompt: InitMFAPrompt:
Title: Установка двухфакторной аутентификации Title: Установка двухфакторной аутентификации
Description: Двухфакторная аутентификация обеспечивает дополнительную защиту вашей учётной записи. Description: Установите двухфакторную аутентификацию для дополнительной защиты вашей учётной записи.
Provider0: Через приложение (например, Google/Microsoft Authenticator, Authy) Provider0: Приложение для кодов (например, Google/Microsoft Authenticator или Authy)
Provider1: Через устройство (например, FaceID, Windows Hello, Fingerprint) Provider1: С помощью устройства (Face ID, Windows Hello, отпечаток пальца)
Provider3: OTP SMS Provider3: Получать код по СМС
Provider4: Электронная почта OTP Provider4: Получать код по электронной почте
NextButtonText: следующий NextButtonText: Продолжить
SkipButtonText: скип SkipButtonText: Пропустить
InitMFAOTP: InitMFAOTP:
Title: Подтверждение двухфакторной аутентификации Title: Настройка двухфакторной аутентификации
Description: Создайте двухфакторную аутентификацию. Загрузите приложение для проверки подлинности, если у вас его ещё нет. Description: Настройте двухфакторную аутентификацию. Загрузите приложение для генерации кодов, если оно у вас отсутствует.
OTPDescription: Отсканируйте код с помощью приложения для проверки подлинности (например, Google/Microsoft Authenticator, Authy) или сгенерируйте код указанного ключа и введите его в поле ниже. OTPDescription: Отсканируйте QR-код с помощью приложения (например, Google Authenticator, Microsoft Authenticator или Authy), либо скопируйте секретный ключ и введите код ниже.
SecretLabel: Ключ SecretLabel: Секретный ключ
CodeLabel: Код CodeLabel: Код подтверждения
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
InitMFAOTPSMS: InitMFAOTPSMS:
Title: 2-факторная верификация Title: Настройка двухфакторной аутентификации
DescriptionPhone: Создайте свой 2-фактор. Введите свой номер телефона, чтобы подтвердить его. DescriptionPhone: Введите номер телефона для его подтверждения.
DescriptionCode: Создайте свой 2-фактор. Введите полученный код, чтобы подтвердить свой номер телефона. DescriptionCode: Введите код из СМС для подтверждения номера телефона.
PhoneLabel: Телефон PhoneLabel: Номер телефона
CodeLabel: Код CodeLabel: Код из СМС
EditButtonText: редактировать EditButtonText: Изменить
ResendButtonText: Повторная отправка кода ResendButtonText: Отправить код ещё раз
NextButtonText: следующий NextButtonText: Продолжить
InitMFAU2F: InitMFAU2F:
Title: Добавление ключа безопасности Title: Добавление ключа безопасности
Description: Ключ безопасности — это метод проверки, который можно встроить в телефон, используя Bluetooth, или подключить непосредственно к USB-порту компьютера. Description: Ключ безопасности — это метод проверки, который может быть встроен в телефон, использовать Bluetooth или подключаться напрямую к USB-порту вашего компьютера.
TokenNameLabel: Название ключа безопасности / устройства TokenNameLabel: Название ключа безопасности / устройства
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлён или используйте другой (например, Chrome, Safari, Firefox) NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
RegisterTokenButtonText: Добавить ключ безопасности RegisterTokenButtonText: Добавить ключ безопасности
ErrorRetry: Повторите попытку или выберите другой метод. ErrorRetry: Повторите попытку, создайте новый запрос или выберите другой метод.
InitMFADone: InitMFADone:
Title: Ключ безопасности подтверждён Title: Ключ безопасности подтверждён
Description: Поздравляю! Вы только что успешно настроили двухфакторную аутентификацию и сделали свою учётную запись более безопасной. Фактор необходимо вводить при каждом входе в систему. Description: Отлично! Вы успешно настроили двухфакторную аутентификацию и сделали свою учётную запись более безопасной. Фактор необходимо вводить при каждом входе в систему.
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
MFAProvider: MFAProvider:
Provider0: Через приложение (например, Google/Microsoft Authenticator, Authy) Provider0: Приложение для кодов (например, Google/Microsoft Authenticator или Authy)
Provider1: Через устройство (например, FaceID, Windows Hello, Fingerprint) Provider1: С помощью устройства (Face ID, Windows Hello, отпечаток пальца)
Provider3: OTP SMS Provider3: Получать код по СМС
Provider4: Электронная почта OTP Provider4: Получать код по электронной почте
ChooseOther: или выберите другой вариант ChooseOther: или выберите другой вариант
VerifyMFAOTP: VerifyMFAOTP:
Title: Подтверждение двухфакторной аутентификации Title: Подтверждение двухфакторной аутентификации
Description: Подтвердите двухфакторную аутентификацию. Description: Введите код для проверки второго фактора
CodeLabel: Код CodeLabel: Код
NextButtonText: далее NextButtonText: Продолжить
VerifyOTP: VerifyOTP:
Title: Проверка 2-фактора Title: Подтверждение двухфакторной аутентификации
Description: Проверьте свой второй фактор Description: Введите код для проверки второго фактора
CodeLabel: Код CodeLabel: Код
ResendButtonText: Повторная отправка кода ResendButtonText: Отправить код ещё раз
NextButtonText: следующий NextButtonText: Продолжить
VerifyMFAU2F: VerifyMFAU2F:
Title: Подтверждение двухфакторной аутентификации Title: Подтверждение двухфакторной аутентификации
Description: Подтвердите двухфакторную аутентификацию с помощью зарегистрированного устройства (например, FaceID, Windows Hello, Fingerprint). Description: Подтвердите двухфакторную аутентификацию с помощью зарегистрированного устройства (например, FaceID, Windows Hello или отпечатка пальца).
NotSupported: WebAuthN не поддерживается вашим браузером. Убедитесь, что вы используете самую новую версию, или измените браузер на поддерживаемый (Chrome, Safari, Firefox) NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
ErrorRetry: Повторите попытку или выберите другой метод. ErrorRetry: Повторите попытку или выберите другой метод.
ValidateTokenButtonText: Подтвердить двухфакторную аутентификацию ValidateTokenButtonText: Подтвердить
Passwordless: Passwordless:
Title: Вход без пароля Title: Вход без пароля
Description: Войдите в систему с помощью методов аутентификации, предоставляемых вашим устройством, таких как FaceID, Windows Hello или Fingerprint. Description: Войдите в систему с помощью методов аутентификации, доступных на вашем устройстве, таких как FaceID, Windows Hello или отпечаток пальца.
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлён или используйте другой (например, Chrome, Safari, Firefox) NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
ErrorRetry: Повторите попытку или выберите другой метод. ErrorRetry: Повторите попытку или выберите другой метод.
LoginWithPwButtonText: Войти по паролю LoginWithPwButtonText: Войти с паролем
ValidateTokenButtonText: Войти без пароля ValidateTokenButtonText: Войти без пароля
PasswordlessPrompt: PasswordlessPrompt:
Title: Установка входа без пароля Title: Настройка входа без пароля
Description: Хотите настроить вход без пароля? (Например, используя методы аутентификации вашего устройства, такие как FaceID, Windows Hello или Fingerprint). Description: Хотите настроить вход без пароля? Вы сможете использовать методы аутентификации вашего устройства, такие как FaceID, Windows Hello или отпечаток пальца.
DescriptionInit: Вам необходимо настроить вход без пароля. Воспользуйтесь ссылкой, которую вы получили, чтобы зарегистрировать своё устройство. DescriptionInit: Для начала настройки входа без пароля перейдите по полученной ссылке и зарегистрируйте своё устройство.
PasswordlessButtonText: Перейти к входу без пароля PasswordlessButtonText: Настроить вход без пароля
NextButtonText: далее NextButtonText: Продолжить
SkipButtonText: пропустить SkipButtonText: Пропустить
PasswordlessRegistration: PasswordlessRegistration:
Title: Установка входа без пароля Title: Настройка входа без пароля
Description: Добавьте свою аутентификацию, указав имя (например, MyMobilePhone, MacBook и так далее), а затем нажмите кнопку «Зарегистрировать вход без пароля» ниже. Description: Укажите имя устройства (например, MyMobilePhone, MacBook и так далее), а затем нажмите кнопку «Зарегистрировать вход без пароля» ниже.
TokenNameLabel: Название устройства TokenNameLabel: Имя устройства
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлён или используйте другой (например, Chrome, Safari, Firefox) NotSupported: Ваш браузер не поддерживает WebAuthN. Пожалуйста, обновите его или используйте другой (например, Chrome, Safari, Firefox).
RegisterTokenButtonText: Зарегистрировать вход без пароля RegisterTokenButtonText: Зарегистрировать вход без пароля
ErrorRetry: Повторите попытку или выберите другой метод. ErrorRetry: Повторите попытку или выберите другой метод.
PasswordlessRegistrationDone: PasswordlessRegistrationDone:
Title: Установка входа без пароля Title: Вход без пароля настроен
Description: Устройство для входа без пароля успешно добавлено. Description: Устройство для входа без пароля успешно зарегистрировано.
DescriptionClose: Теперь вы можете закрыть данное окно. DescriptionClose: Теперь вы можете закрыть это окно.
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
PasswordChange: PasswordChange:
Title: Изменение пароля Title: Изменение пароля
Description: Измените ваш пароль. Введите старый и новый пароли. Description: Введите текущий пароль и задайте новый.
ExpiredDescription: Срок действия вашего пароля истек, и его необходимо изменить. Введите старый и новый пароль. ExpiredDescription: Срок действия вашего пароля истёк. Пожалуйста, введите текущий пароль и задайте новый.
OldPasswordLabel: Старый пароль OldPasswordLabel: Текущий пароль
NewPasswordLabel: Новый пароль NewPasswordLabel: Новый пароль
NewPasswordConfirmLabel: Подтверждение пароля NewPasswordConfirmLabel: Повторите новый пароль
CancelButtonText: отмена CancelButtonText: Отмена
NextButtonText: далее NextButtonText: Продолжить
Footer: Нижний колонтитул Footer: Примечание
PasswordChangeDone: PasswordChangeDone:
Title: Изменение пароля Title: Изменение пароля
Description: Ваш пароль был успешно изменён. Description: Ваш пароль был успешно изменён.
NextButtonText: далее NextButtonText: Продолжить
PasswordResetDone: PasswordResetDone:
Title: Ссылка для сброса пароля отправлена Title: Ссылка для сброса пароля отправлена
Description: Проверьте вашу электронную почту, чтобы сбросить пароль. Description: Проверьте вашу электронную почту, чтобы сбросить пароль.
NextButtonText: далее NextButtonText: Продолжить
EmailVerification: EmailVerification:
Title: Подтверждение электронной почты Title: Подтверждение электронной почты
Description: Мы отправили вам письмо для подтверждения вашей электронной почты. Пожалуйста, введите полученный код в поле ниже. Description: Мы отправили письмо с кодом для подтверждения вашей электронной почты. Введите код ниже.
CodeLabel: Код CodeLabel: Код
NextButtonText: далее NextButtonText: Продолжить
ResendButtonText: повторно отправить код ResendButtonText: Отправить код ещё раз
EmailVerificationDone: EmailVerificationDone:
Title: Подтверждение электронной почты Title: Подтверждение электронной почты
Description: Ваша электронная почта была успешно подтверждена. Description: Ваша электронная почта успешно подтверждена.
NextButtonText: далее NextButtonText: Продолжить
CancelButtonText: отмена CancelButtonText: Отмена
LoginButtonText: вход LoginButtonText: Войти
RegisterOption: RegisterOption:
Title: Способы регистрации Title: Способы регистрации
Description: Выберите способ регистрации. Description: Выберите способ регистрации.
RegisterUsernamePasswordButtonText: С паролем логина RegisterUsernamePasswordButtonText: С логином и паролем
ExternalLoginDescription: или зарегистрируйтесь внешним пользователем ExternalLoginDescription: или зарегистрируйтесь с помощью внешнего пользователя
LoginButtonText: вход LoginButtonText: Войти
RegistrationUser: RegistrationUser:
Title: Регистрация Title: Регистрация
Description: Введите ваши данные. Электронная почта будет использоваться в качестве логина. Description: Введите свои данные. Электронная почта будет использоваться как логин.
DescriptionOrgRegister: Введите ваши данные. DescriptionOrgRegister: Введите свои данные.
EmailLabel: Электронная почта EmailLabel: Электронная почта
UsernameLabel: Логин UsernameLabel: Логин
FirstnameLabel: Имя FirstnameLabel: Имя
@ -263,24 +264,25 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Пол GenderLabel: Пол
Female: Женский Female: Женский
Male: Мужской Male: Мужской
Diverse: Другой / X Diverse: Другой / X
PasswordLabel: Пароль PasswordLabel: Пароль
PasswordConfirmLabel: Подтверждение пароля PasswordConfirmLabel: Повторите пароль
TosAndPrivacyLabel: Условия использования TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности PrivacyLinkText: Политикой конфиденциальности
ExternalLogin: или зарегистрируйтесь внешним пользователем ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя
BackButtonText: вход BackButtonText: Войти
NextButtonText: далее NextButtonText: Продолжить
ExternalRegistrationUserOverview: ExternalRegistrationUserOverview:
Title: Регистрация внешнего пользователя Title: Регистрация внешнего пользователя
Description: Мы получили ваши данные пользователя у выбранного провайдера. Теперь вы можете изменить или дополнить их. Description: Мы получили ваши данные от провайдера. Вы можете изменить или дополнить их.
EmailLabel: Электронная почта EmailLabel: Электронная почта
UsernameLabel: Логин UsernameLabel: Логин
FirstnameLabel: Имя FirstnameLabel: Имя
@ -305,14 +307,15 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Условия использования TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением TosLinkText: Пользовательским соглашением
PrivacyConfirm: Я согласен с PrivacyConfirm: Я согласен с
PrivacyLinkText: Политикой конфиденциальности PrivacyLinkText: Политикой конфиденциальности
ExternalLogin: или зарегистрируйтесь внешним пользователем ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя
BackButtonText: назад BackButtonText: Назад
NextButtonText: сохранить NextButtonText: Сохранить
RegistrationOrg: RegistrationOrg:
Title: Регистрация организации Title: Регистрация организации
@ -323,7 +326,7 @@ RegistrationOrg:
FirstnameLabel: Имя FirstnameLabel: Имя
LastnameLabel: Фамилия LastnameLabel: Фамилия
PasswordLabel: Пароль PasswordLabel: Пароль
PasswordConfirmLabel: Подтверждение пароля PasswordConfirmLabel: Повторите пароль
TosAndPrivacyLabel: Условия использования TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением TosLinkText: Пользовательским соглашением
@ -333,32 +336,32 @@ RegistrationOrg:
LoginSuccess: LoginSuccess:
Title: Успешный вход Title: Успешный вход
AutoRedirectDescription: Вы будете автоматически перенаправлены в своё приложение. Если этого не произошло, нажмите кнопку ниже. После этого вы можете закрыть окно. AutoRedirectDescription: Вы будете автоматически перенаправлены в приложение. Если этого не произошло, нажмите кнопку ниже.
RedirectedDescription: Теперь вы можете закрыть данное окно. RedirectedDescription: Вы можете закрыть это окно.
NextButtonText: далее NextButtonText: Продолжить
LogoutDone: LogoutDone:
Title: Выход из системы Title: Выход из системы
Description: Вы успешно вышли из системы. Description: Вы успешно вышли из системы.
LoginButtonText: вход LoginButtonText: Войти
LinkingUserPrompt: LinkingUserPrompt:
Title: Существующий пользователь найден Title: Найден существующий пользователь
Description: "Хотите ли вы связать существующую учетную запись:" Description: "Хотите связать эту учётную запись с существующей?"
LinkButtonText: Связь LinkButtonText: Связать
OtherButtonText: Другие варианты OtherButtonText: Другие варианты
LinkingUsersDone: LinkingUsersDone:
Title: Привязка пользователя Title: Привязка пользователя
Description: Привязка пользователя выполнена. Description: Привязка учётной записи выполнена успешно.
CancelButtonText: отмена CancelButtonText: Отмена
NextButtonText: далее NextButtonText: Продолжить
ExternalNotFound: ExternalNotFound:
Title: Внешний пользователь не найден Title: Внешний пользователь не найден
Description: Внешний пользователь не найден. Вы можете привязать своего пользователя или автоматически зарегистрировать нового. Description: Мы не смогли найти указанного внешнего пользователя. Вы можете привязать существующую учетную запись или зарегистрировать нового пользователя.
LinkButtonText: Привязать LinkButtonText: Привязать
AutoRegisterButtonText: зарегистрировать AutoRegisterButtonText: Зарегистрировать
TosAndPrivacyLabel: Условия использования TosAndPrivacyLabel: Условия использования
TosConfirm: Я согласен с TosConfirm: Я согласен с
TosLinkText: Пользовательским соглашением TosLinkText: Пользовательским соглашением
@ -381,137 +384,138 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Авторизация устройства Title: Авторизация устройства
UserCode: UserCode:
Label: Код пользователя Label: Код пользователя
Description: Введите код пользователя, представленный на устройстве. Description: Введите код, отображаемый на вашем устройстве.
ButtonNext: следующий ButtonNext: Продолжить
Action: Action:
Description: Предоставьте доступ к устройству. Description: Предоставьте устройству доступ к системе.
GrantDevice: Вы собираетесь предоставить устройство GrantDevice: вы собираетесь предоставить устройству
AccessToScopes: Доступ к следующим областям AccessToScopes: доступ к следующим областям
Button: Button:
Allow: разрешать Allow: Разрешать
Deny: отрицать Deny: Отклонить
Done: Done:
Description: Договорились. Description: Операция завершена.
Approved: Авторизация устройства одобрена. Теперь вы можете вернуться к устройству. Approved: Устройство успешно авторизовано. Теперь вы можете вернуться к устройству.
Denied: Отказано в авторизации устройства. Теперь вы можете вернуться к устройству. Denied: Авторизация устройства отклонена. Теперь вы можете вернуться к устройству.
Footer: Footer:
PoweredBy: На базе PoweredBy: Работает на основе
Tos: Пользовательское соглашение Tos: Пользовательское соглашение
PrivacyPolicy: Политика конфиденциальности PrivacyPolicy: Политика конфиденциальности
Help: Помощь Help: Помощь
SupportEmail: Электронная почта службы поддержки SupportEmail: Электронная почта службы поддержки
SignIn: Вход с помощью {{.Provider}} SignIn: Войти с помощью {{.Provider}}
Errors: Errors:
Internal: Произошла внутренняя ошибка Internal: Произошла внутренняя ошибка
AuthRequest: AuthRequest:
NotFound: Не удалось обнаружить запрос авторизации NotFound: authrequest не найден
UserAgentNotCorresponding: User Agent не соответствует UserAgentNotCorresponding: User Agent не совпадает
UserAgentNotFound: ID User Agent не найден UserAgentNotFound: User Agent ID не найден
TokenNotFound: Токен не найден TokenNotFound: Токен не найден
RequestTypeNotSupported: Тип запроса не поддерживается RequestTypeNotSupported: Тип запроса не поддерживается
MissingParameters: Отсутствуют обязательные параметры MissingParameters: Отсутствуют обязательные параметры
User: User:
NotFound: Пользователь не может быть найден NotFound: Пользователь не найден
AlreadyExists: Пользователь уже существует AlreadyExists: Пользователь уже существует
Inactive: Пользователь неактивен Inactive: Пользователь неактивен
NotFoundOnOrg: Не удалось найти пользователя в выбранной организации NotFoundOnOrg: Не удалось найти пользователя в выбранной организации
NotAllowedOrg: Пользователь не является участником требуемой организации NotAllowedOrg: Пользователь не является членом требуемой организации
NotMatchingUserID: Пользователь не совпадает с пользователем в запросе авторизации NotMatchingUserID: Указанный пользователь не совпадает с пользователем в authrequest
UserIDMissing: UserID пустой UserIDMissing: Не указан UserID
Invalid: Неверные данные пользователя Invalid: Неверные данные пользователя
DomainNotAllowedAsUsername: Домен уже зарезервирован и не может быть использован DomainNotAllowedAsUsername: Домен уже зарезервирован и не может быть использован в качестве логина
NotAllowedToLink: Пользователя не разрешено привязывать к внешнему провайдеру входа NotAllowedToLink: Привязка пользователя к внешнему провайдеру запрещена
Profile: Profile:
NotFound: Профиль не найден NotFound: Профиль не найден
NotChanged: Профиль не изменен NotChanged: Профиль не изменен
Empty: Профиль пуст Empty: Профиль пуст
FirstNameEmpty: Имя в профиле пусто FirstNameEmpty: Поле имени пусто
LastNameEmpty: Фамилия в профиле пуста LastNameEmpty: Поле фамилии пусто
IDMissing: Отсутствует идентификатор профиля IDMissing: Отсутствует идентификатор профиля
Email: Email:
NotFound: Электронная почта не найдена NotFound: Электронная почта не найдена
Invalid: Адрес электронной почты недействителен Invalid: Некорректный адрес электронной почты
AlreadyVerified: Электронная почта уже подтверждена AlreadyVerified: Электронная почта уже подтверждена
NotChanged: Адрес электронной почты не изменился NotChanged: Адрес электронной почты не изменился
Empty: Электронная почта пуста Empty: Электронная почта не указана
IDMissing: Отсутствует идентификатор электронной почты IDMissing: Отсутствует идентификатор электронной почты
Phone: Phone:
NotFound: Телефон не найден NotFound: Номер телефона не найден
Invalid: Телефон недействителен Invalid: Неверный номер телефона
AlreadyVerified: Телефон уже проверен AlreadyVerified: Номер телефона уже подтвержден
Empty: Телефон пуст Empty: Номер телефона не указан
NotChanged: Телефон не менялся NotChanged: Номер телефона не изменился
Address: Address:
NotFound: Адрес не найден NotFound: Адрес не найден
NotChanged: Адрес не изменился NotChanged: Адрес не изменился
Username: Username:
AlreadyExists: Имя пользователя уже занято AlreadyExists: Логин уже занят
Reserved: Имя пользователя уже занято Reserved: Логин уже занят
Empty: Имя пользователя пусто Empty: Логин не указан
Password: Password:
ConfirmationWrong: Неверное подтверждение пароля ConfirmationWrong: Указанные пароли не совпадают
Empty: Пароль пустой Empty: Пароль не указан
Invalid: Неверный пароль Invalid: Неверный пароль
InvalidAndLocked: Неверный пароль, пользователь заблокирован. Обратитесь к администратору. InvalidAndLocked: Неверный пароль, пользователь заблокирован. Обратитесь к администратору.
NotChanged: Пароль не изменен NotChanged: Пароль не изменился
UsernameOrPassword: UsernameOrPassword:
Invalid: Логин или пароль недействительны Invalid: Неверный логин или пароль
PasswordComplexityPolicy: PasswordComplexityPolicy:
NotFound: Политика паролей не найдена NotFound: Политика паролей не найдена
MinLength: Пароль слишком короткий MinLength: Пароль слишком короткий
HasLower: Пароль должен содержать строчную букву HasLower: Пароль должен содержать хотя бы одну строчную букву
HasUpper: Пароль должен содержать заглавную букву HasUpper: Пароль должен содержать хотя бы одну заглавную букву
HasNumber: Пароль должен содержать цифру HasNumber: Пароль должен содержать хотя бы одну цифру
HasSymbol: Пароль должен содержать символ HasSymbol: Пароль должен содержать хотя бы один специальный символ
Code: Code:
Expired: Код истёк Expired: Код истёк
Invalid: Неверный код Invalid: Неверный код
Empty: Код пустой Empty: Код не указан
CryptoCodeNil: Криптокод равен нулю CryptoCodeNil: Пустой криптокод
NotFound: Не удалось найти код NotFound: Код не найден
GeneratorAlgNotSupported: Неподдерживаемый алгоритм генератора GeneratorAlgNotSupported: Алгоритм генерации кода не поддерживается
EmailVerify: EmailVerify:
UserIDEmpty: UserID пустой UserIDEmpty: UserID не указан
ExternalData: ExternalData:
CouldNotRead: Внешние данные не могут быть обработаны корректно CouldNotRead: Не удалось обработать внешние данные
MFA: MFA:
NoProviders: Нет доступных многофакторных поставщиков NoProviders: Нет доступных методов многофакторной аутентификации
OTP: OTP:
AlreadyReady: Мультифактор OTP (OneTimePassword) уже настроен AlreadyReady: OTP (OneTimePassword) уже настроен
NotExisting: Мультифактор OTP (OneTimePassword) не существует NotExisting: OTP (OneTimePassword) не существует
InvalidCode: Неверный код InvalidCode: Неверный код
NotReady: Мультифактор OTP (OneTimePassword) не готов NotReady: OTP (OneTimePassword) не готов
Locked: Пользователь заблокирован Locked: Пользователь заблокирован
SomethingWentWrong: Что-то пошло не так SomethingWentWrong: Что-то пошло не так
NotActive: Пользователь неактивен NotActive: Пользователь неактивен
ExternalIDP: ExternalIDP:
IDPTypeNotImplemented: IDP тип не реализован IDPTypeNotImplemented: Тип внешнего провайдера не поддерживается
NotAllowed: Внешний провайдер входа запрещён NotAllowed: Доступ к внешнему провайдеру запрещен
IDPConfigIDEmpty: IDP ID пустой IDPConfigIDEmpty: Не указан идентификатор конфигурации провайдера
ExternalUserIDEmpty: External User ID пустой ExternalUserIDEmpty: Не указан внешний идентификатор пользователя
UserDisplayNameEmpty: Отображаемое имя пользователя пустое UserDisplayNameEmpty: Отображаемое имя пользователя не указано
NoExternalUserData: Данные внешнего пользователя не получены NoExternalUserData: Внешние данные пользователя отсутствуют
CreationNotAllowed: Создание нового пользователя для данного провайдера не разрешено CreationNotAllowed: Создание нового пользователя для этого провайдера запрещено
LinkingNotAllowed: Привязка пользователя с данным провайдером запрещена LinkingNotAllowed: Привязка к этому провайдеру запрещена
NoOptionAllowed: Ни создание, ни связывание не разрешены для этого провайдера. Пожалуйста, обратитесь к администратору. NoOptionAllowed: Ни создание, ни привязка пользователя к этому провайдеру невозможны. Обратитесь к администратору.
GrantRequired: Вход невозможен. Пользователь должен иметь хотя бы один допуск в приложении. Пожалуйста, свяжитесь с вашим администратором. GrantRequired: Вход невозможен. Пользователь должен иметь хотя бы один допуск к приложению. Обратитесь к администратору.
ProjectRequired: Вход невозможен. Организация пользователя должна иметь допуск к проекту. Пожалуйста, свяжитесь с вашим администратором. ProjectRequired: Вход невозможен. Организация пользователя должна иметь доступ к проекту. Обратитесь к администратору.
IdentityProvider: IdentityProvider:
InvalidConfig: Недопустимая конфигурация поставщика идентификационных данных InvalidConfig: Некорректная конфигурация провайдера идентификации
IAM: IAM:
LockoutPolicy: LockoutPolicy:
NotExisting: Политика блокировки не существует NotExisting: Политика блокировки не найдена
Org: Org:
LoginPolicy: LoginPolicy:
RegistrationNotAllowed: Регистрация не допускается RegistrationNotAllowed: Регистрация запрещена
DeviceAuth: DeviceAuth:
NotExisting: Код пользователя не существует NotExisting: Код пользователя не существует

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: Kön GenderLabel: Kön
Female: Man Female: Man
Male: Kvinna Male: Kvinna
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Användarvillkor TosAndPrivacyLabel: Användarvillkor
TosConfirm: Jag accepterar TosConfirm: Jag accepterar
TosLinkText: Användarvillkoren TosLinkText: Användarvillkoren
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: Tillgång från hårdvaruenhet Title: Tillgång från hårdvaruenhet
UserCode: UserCode:

View File

@ -264,6 +264,7 @@ RegistrationUser:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
GenderLabel: 性别 GenderLabel: 性别
Female: 女性 Female: 女性
Male: 男性 Male: 男性
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: 条款和条款 TosAndPrivacyLabel: 条款和条款
TosConfirm: 我接受 TosConfirm: 我接受
TosLinkText: 服务条款 TosLinkText: 服务条款
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska Swedish: Svenska
Indonesian: Bahasa Indonesia Indonesian: Bahasa Indonesia
Hungarian: Magyar Hungarian: Magyar
Korean: 한국어
DeviceAuth: DeviceAuth:
Title: 设备授权 Title: 设备授权
UserCode: UserCode:

View File

@ -98,6 +98,8 @@
</option> </option>
<option value="hu" id="hu" {{if (selectedLanguage "hu")}} selected {{end}}>{{t "ExternalNotFound.Hungarian"}} <option value="hu" id="hu" {{if (selectedLanguage "hu")}} selected {{end}}>{{t "ExternalNotFound.Hungarian"}}
</option> </option>
<option value="ko" id="ko" {{if (selectedLanguage "ko")}} selected {{end}}>{{t "ExternalNotFound.Korean"}}
</option>
</select> </select>
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@ import (
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/idp/providers/saml"
"github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
@ -511,10 +512,10 @@ func (c *Commands) UpdateInstanceAppleProvider(ctx context.Context, id string, p
return pushedEventsToObjectDetails(pushedEvents), nil return pushedEventsToObjectDetails(pushedEvents), nil
} }
func (c *Commands) AddInstanceSAMLProvider(ctx context.Context, provider SAMLProvider) (string, *domain.ObjectDetails, error) { func (c *Commands) AddInstanceSAMLProvider(ctx context.Context, provider *SAMLProvider) (id string, details *domain.ObjectDetails, err error) {
instanceID := authz.GetInstance(ctx).InstanceID() instanceID := authz.GetInstance(ctx).InstanceID()
instanceAgg := instance.NewAggregate(instanceID) instanceAgg := instance.NewAggregate(instanceID)
id, err := c.idGenerator.Next() id, err = c.idGenerator.Next()
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -530,7 +531,7 @@ func (c *Commands) AddInstanceSAMLProvider(ctx context.Context, provider SAMLPro
return id, pushedEventsToObjectDetails(pushedEvents), nil return id, pushedEventsToObjectDetails(pushedEvents), nil
} }
func (c *Commands) UpdateInstanceSAMLProvider(ctx context.Context, id string, provider SAMLProvider) (*domain.ObjectDetails, error) { func (c *Commands) UpdateInstanceSAMLProvider(ctx context.Context, id string, provider *SAMLProvider) (details *domain.ObjectDetails, err error) {
instanceID := authz.GetInstance(ctx).InstanceID() instanceID := authz.GetInstance(ctx).InstanceID()
instanceAgg := instance.NewAggregate(instanceID) instanceAgg := instance.NewAggregate(instanceID)
writeModel := NewSAMLInstanceIDPWriteModel(instanceID, id) writeModel := NewSAMLInstanceIDPWriteModel(instanceID, id)
@ -1719,7 +1720,7 @@ func (c *Commands) prepareUpdateInstanceAppleProvider(a *instance.Aggregate, wri
} }
} }
func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeModel *InstanceSAMLIDPWriteModel, provider SAMLProvider) preparation.Validation { func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeModel *InstanceSAMLIDPWriteModel, provider *SAMLProvider) preparation.Validation {
return func() (preparation.CreateCommands, error) { return func() (preparation.CreateCommands, error) {
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-o07zjotgnd", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "INST-o07zjotgnd", "Errors.Invalid.Argument")
@ -1734,6 +1735,9 @@ func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeMo
if len(provider.Metadata) == 0 { if len(provider.Metadata) == 0 {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-3bi3esi16t", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "INST-3bi3esi16t", "Errors.Invalid.Argument")
} }
if _, err := saml.ParseMetadata(provider.Metadata); err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "INST-SF3rwhgh", "Errors.Project.App.SAMLMetadataFormat")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
events, err := filter(ctx, writeModel.Query()) events, err := filter(ctx, writeModel.Query())
if err != nil { if err != nil {
@ -1772,7 +1776,7 @@ func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeMo
} }
} }
func (c *Commands) prepareUpdateInstanceSAMLProvider(a *instance.Aggregate, writeModel *InstanceSAMLIDPWriteModel, provider SAMLProvider) preparation.Validation { func (c *Commands) prepareUpdateInstanceSAMLProvider(a *instance.Aggregate, writeModel *InstanceSAMLIDPWriteModel, provider *SAMLProvider) preparation.Validation {
return func() (preparation.CreateCommands, error) { return func() (preparation.CreateCommands, error) {
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-7o3rq1owpm", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "INST-7o3rq1owpm", "Errors.Invalid.Argument")
@ -1790,6 +1794,9 @@ func (c *Commands) prepareUpdateInstanceSAMLProvider(a *instance.Aggregate, writ
} }
provider.Metadata = data provider.Metadata = data
} }
if _, err := saml.ParseMetadata(provider.Metadata); err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "INST-dsfj3kl2", "Errors.Project.App.SAMLMetadataFormat")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
events, err := filter(ctx, writeModel.Query()) events, err := filter(ctx, writeModel.Query())
if err != nil { if err != nil {

View File

@ -22,6 +22,73 @@ import (
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
var (
validSAMLMetadata = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8080/saml/v2/metadata" ID="_8b02ecf6-aea4-4eda-96c6-190551f05b07">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<CanonicalizationMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
<Reference xmlns="http://www.w3.org/2000/09/xmldsig#" URI="#_8b02ecf6-aea4-4eda-96c6-190551f05b07">
<Transforms xmlns="http://www.w3.org/2000/09/xmldsig#">
<Transform xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
<Transform xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue xmlns="http://www.w3.org/2000/09/xmldsig#">Tyw4csdpNNq0E7wi5FXWdVNkdPNg+cM6kK21VB2+iF0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">hWQSYmnBJENy/okk2qRDuHaZiyqpDsdV6BF9/T/LNjUh/8z4dV2NEZvkNhFEyQ+bqdj+NmRWvKqpg1dtgNJxQc32+IsLQvXNYyhMCtyG570/jaTOtm8daV4NKJyTV7SdwM6yfXgubz5YCRTyV13W2gBIFYppIRImIv5NDcjz+lEmWhnrkw8G2wRSFUY7VvkDn9rgsTzw/Pnsw6hlzpjGDYPMPx3ux3kjFVevdhFGNo+VC7t9ozruuGyH3yue9Re6FZoqa4oyWaPSOwei0ZH6UNqkX93Eo5Y49QKwaO8Rm+kWsOhdTqebVmCc+SpWbbrZbQj4nSLgWGlvCkZSivmH7ezr4Ol1ZkRetQ92UQ7xJS7E0y6uXAGvdgpDnyqHCOFfhTS6yqltHtc3m7JZex327xkv6e69uAEOSiv++sifVUIE0h/5u3hZLvwmTPrkoRVY4wgZ4ieb86QPvhw4UPeYapOhCBk5RfjoEFIeYwPUw5rtOlpTyeBJiKMpH1+mDAoa+8HQytZoMrnnY1s612vINtY7jU5igMwIk6MitQpRGibnBVBHRc2A6aE+XS333ganFK9hX6TzNkpHUb66NINDZ8Rgb1thn3MABArGlomtM5/enrAixWExZp70TSElor7SBdBW57H7OZCYUCobZuPRDLsCO6LLKeVrbdygWeRqr/o=</SignatureValue>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Certificate xmlns="http://www.w3.org/2000/09/xmldsig#">MIIFIjCCAwqgAwIBAgICA7YwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjc0NFoXDTI1MTEyNzE2Mjc0NFowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIG1ldGFkYXRhMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApEpYT7EjbRBp0Hw7PGCiSgUoJtwd2nwZOhGy5WZVWvraAtHzW5ih2B6UwEShjwCmRJZeKYEN9JKJbpAy2EdL/l2rm/pArVNvSQu6sN4izz5p2rd9NfHAO3/EcvYdrelWLQj8WQx6LVM282Z4wbclp8Jz1y8Ow43352hGfFVc1x8gauoNl5MAy4kdbvs8UqihqcRmEyIOWl6UwTApb+XIRSRz0Yop99Fv9ALJwfUppsx+d4j9rlRDvrQJMJz7GC/19L9INTbY0HsVEiTltdAWHwREwrpwxNJQt42p3W/zpf1mjwXd3qNNDZAr1t2POPP4SXd598kabBZ3EMWGGxFw+NYYajyjG5EFOZw09FFJn2jIcovejvigfdqem5DGPECvHefqcqHkBPGukI3RaotXpAYyAGfnV7slVytSW484IX3KloAJLICbETbFGGsGQzIDw8rUqWyaOCOttw2fVNDyRFUMHrGe1PhJ9qA1If+KCWYD0iJqF03rIEhdrvNSdQNYkRa0DdtpacQLpzQtqsUioODqX0W3uzLceJEXLBbU0ZEk8mWZM/auwMo3ycPNXDVwrb6AkUKar+sqSumUuixw7da3KF1/mynh6M2Eo4NRB16oUiyN0EYrit/RRJjsTdH+71cj0V+8KqO88cBpmm+lO6x4RM5xpOf/EwwQHivxgRkCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQBz+7R99uX1Us9T4BB2RK3RD9K8Q5foNmxJ8GbxpOQFL8IG1DE3FqBssciJkOsKY+1+Y6eow2TgmD9MxfCY444C8k8YDDjxIcs+4dEaWMUxA6NoEy378ciy0U1E6rpYLxWYTxXmsELyODWwTrRNIiWfbBD2m0w9HYbK6QvX6IYQqYoTOJJ3WJKsMCeQ8XhQsJYNINZEq8RsERY/aikOlTWN7ax4Mkr3bfnz1euXGClExCOM6ej4m2I33i4nyYBvvRkRRZRQCfkAQ+5WFVZoVXrQHNe/Oifit7tfLaDuybcjgkzzY3o0YbczzbdV69fVoj53VpR3QQOB+PCF/VJPUMtUFPEC05yH76g24KVBiM/Ws8GaERW1AxgupHSmvTY3GSiwDXQ2NzgDxUHfRHo8rxenJdEcPlGM0DstbUONDSFGLwvGDiidUVtqj1UB4yGL26bgtmwf61G4qsTn9PJMWdRmCeeOf7fmloRxTA0EEey3bulBBHim466tWHUhgOP+g1X0iE7CnwL8aJ//CCiQOAv1O6x5RLyxrmVTehPLr1T8qvnBmxpmuYU0kfbYpO3tMVe7VLabBx0cYh7izClZKHhgEj1w4aE9tIk7nqVAwvVocT3io8RrcKixlnBrFd7RYIuF3+RsYC/kYEgnZYKAig5u2TySgGmJ7nIS24FYW68WDg==</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" WantAuthnRequestsSigned="1" ID="_fd70402c-8a31-4a9a-a4a7-da526524c609" validUntil="2024-12-02T16:54:55.656Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<SingleSignOnService xmlns="urn:oasis:names:tc:SAML:2.0:metadata" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/saml/v2/SSO"></SingleSignOnService>
<SingleSignOnService xmlns="urn:oasis:names:tc:SAML:2.0:metadata" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/v2/SSO"></SingleSignOnService>
<AttributeProfile>urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic</AttributeProfile>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="Email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="SurName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="FullName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="UserName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="UserID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<SingleLogoutService xmlns="urn:oasis:names:tc:SAML:2.0:metadata" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/saml/v2/SLO"></SingleLogoutService>
<SingleLogoutService xmlns="urn:oasis:names:tc:SAML:2.0:metadata" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/v2/SLO"></SingleLogoutService>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<KeyDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>http://localhost:8080/saml/v2/metadata IDP signing</KeyName>
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Certificate xmlns="http://www.w3.org/2000/09/xmldsig#">MIIFIjCCAwqgAwIBAgICA7QwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjUwMloXDTI1MTEyNzE2MjUwMlowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIHJlc3BvbnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2lUgaI6AS/9xvM9DNSWK6Ho64LpK8UIioM26QfvAfeQ/I2pgX6SwWxEbd7qv+PkJzaFTjrXSlwOmWsJYma+UsdyFClaGFRyCgY8SWxPceandC8a+hQIDS/irLd9XF33RWp0b/09HjQl+n0HZ4teUFDUd2U1mUf3XCpn0+Ho316bmi6xSW6zaMy5RsbUl01hgWj2fgapAsGAHSBphwCE3Dz/9I/UfHWQw1k2/UTgjc9uIujcza6WgOxfsKluXYIOxwNKTfmzzOJMUwXz6GRgB2jhQI29MuKOZOITA7pXq5kZKf0lSRU8zKFTMJaK4zAHQ6f877Drr8XdAHemuXGZ2JdH/Dbdwarzy3YBMCWsAYlpeEvaVAdiSpyR7fAZktNuHd39Zg00Vlj2wdc44Vk5yVssW7pv5qnVZ7JTrXX2uBYFecLAXmplQ2ph1VdSXZLEDGgjiNA2T/fBj7G4/VjsuCBZFm1I0KCJp3HWEJx5dwwhSVc5wOJEzl7fMuPYMKWH/RM6P/7LnO1ulpdmiKPa4gHzdg3hDZn42NKcVt3UYf0phtxpWMrZp/DUEeizhckrC4ed6cfGtS3CUtJEqoycrCROJ5Hy+ONHl5Aqxt+JoPU+t/XATuctfPxQVcDr0itHzo2cjh/AVTU+IC7C0oQHSS9CC8Fp58UqbtYwFtSAd7ecCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQAp+IGZScVIbRdCq5HPjlYBPOY7UbL8ZXnlMW/HLELV9GndnULuFhnuQTIdA5dquCsk8RI1fKsScEV1rqWvHZeSo5nVbvUaPJctoD/4GACqE6F8axs1AgSOvpJMyuycjSzSh6gDM1z37Fdqc/2IRqgi7SKdDsfJpi8XW8LtErpp4kyE1rEXopsXG2fe1UH25bZpXraUqYvp61rwVUCazAtV/U7ARG5AnT0mPqzUriIPrfL+v/+2ntV/BSc8/uCqYnHbwpIwjPURCaxo1Pmm6EEkm+V/Ss4ieNwwkD2bLLLST1LoVMim7Ebfy53PEKpsznKsGlVSu0YYKUsStWQVpwhKQw0bQLCJHdpvZtZSDgS9RbSMZz+aY/fpoNx6wDvmMgtdrb3pVXZ8vPKdq9YDrGfFqP60QdZ3CuSHXCM/zX4742GgImJ4KYAcTuF1+BkGf5JLAJOUZBkfCQ/kBT5wr8+EotLxASOC6717whLBYMEG6N8osEk+LDqoJRTLqkzirJsyOHWChKK47yGkdS3HBIZfo91QrJwKpfATYziBjEnqipkTu+6jFylBIkxKTPye4b3vgcodZP8LSNVXAsMGTPNPJxzPWQ37ba4zMnYZ5iUerlaox/SNsn68DT6RajIb1A1JDq+HNFc3hQP2bzk2y5pCax8zo5swjdklnm4clfB2Lw==</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
</IDPSSODescriptor>
<AttributeAuthorityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" ID="_b3fed381-af56-4160-abf5-5ffd1e21cf61" validUntil="2024-12-02T16:54:55.656Z" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<AttributeService xmlns="urn:oasis:names:tc:SAML:2.0:metadata" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8080/saml/v2/attribute"></AttributeService>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<AttributeProfile>urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic</AttributeProfile>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="Email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="SurName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="FullName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="UserName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<Attribute xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Name="UserID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><AttributeValue></AttributeValue></Attribute>
<KeyDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" use="signing">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>http://localhost:8080/saml/v2/metadata IDP signing</KeyName>
<X509Data xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Certificate xmlns="http://www.w3.org/2000/09/xmldsig#">MIIFIjCCAwqgAwIBAgICA7QwDQYJKoZIhvcNAQELBQAwLDEQMA4GA1UEChMHWklUQURFTDEYMBYGA1UEAxMPWklUQURFTCBTQU1MIENBMB4XDTI0MTEyNzEwMjUwMloXDTI1MTEyNzE2MjUwMlowMjEQMA4GA1UEChMHWklUQURFTDEeMBwGA1UEAxMVWklUQURFTCBTQU1MIHJlc3BvbnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2lUgaI6AS/9xvM9DNSWK6Ho64LpK8UIioM26QfvAfeQ/I2pgX6SwWxEbd7qv+PkJzaFTjrXSlwOmWsJYma+UsdyFClaGFRyCgY8SWxPceandC8a+hQIDS/irLd9XF33RWp0b/09HjQl+n0HZ4teUFDUd2U1mUf3XCpn0+Ho316bmi6xSW6zaMy5RsbUl01hgWj2fgapAsGAHSBphwCE3Dz/9I/UfHWQw1k2/UTgjc9uIujcza6WgOxfsKluXYIOxwNKTfmzzOJMUwXz6GRgB2jhQI29MuKOZOITA7pXq5kZKf0lSRU8zKFTMJaK4zAHQ6f877Drr8XdAHemuXGZ2JdH/Dbdwarzy3YBMCWsAYlpeEvaVAdiSpyR7fAZktNuHd39Zg00Vlj2wdc44Vk5yVssW7pv5qnVZ7JTrXX2uBYFecLAXmplQ2ph1VdSXZLEDGgjiNA2T/fBj7G4/VjsuCBZFm1I0KCJp3HWEJx5dwwhSVc5wOJEzl7fMuPYMKWH/RM6P/7LnO1ulpdmiKPa4gHzdg3hDZn42NKcVt3UYf0phtxpWMrZp/DUEeizhckrC4ed6cfGtS3CUtJEqoycrCROJ5Hy+ONHl5Aqxt+JoPU+t/XATuctfPxQVcDr0itHzo2cjh/AVTU+IC7C0oQHSS9CC8Fp58UqbtYwFtSAd7ecCAwEAAaNIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB8GA1UdIwQYMBaAFIzl7uckcPWldirXeOFL3rH6K8FLMA0GCSqGSIb3DQEBCwUAA4ICAQAp+IGZScVIbRdCq5HPjlYBPOY7UbL8ZXnlMW/HLELV9GndnULuFhnuQTIdA5dquCsk8RI1fKsScEV1rqWvHZeSo5nVbvUaPJctoD/4GACqE6F8axs1AgSOvpJMyuycjSzSh6gDM1z37Fdqc/2IRqgi7SKdDsfJpi8XW8LtErpp4kyE1rEXopsXG2fe1UH25bZpXraUqYvp61rwVUCazAtV/U7ARG5AnT0mPqzUriIPrfL+v/+2ntV/BSc8/uCqYnHbwpIwjPURCaxo1Pmm6EEkm+V/Ss4ieNwwkD2bLLLST1LoVMim7Ebfy53PEKpsznKsGlVSu0YYKUsStWQVpwhKQw0bQLCJHdpvZtZSDgS9RbSMZz+aY/fpoNx6wDvmMgtdrb3pVXZ8vPKdq9YDrGfFqP60QdZ3CuSHXCM/zX4742GgImJ4KYAcTuF1+BkGf5JLAJOUZBkfCQ/kBT5wr8+EotLxASOC6717whLBYMEG6N8osEk+LDqoJRTLqkzirJsyOHWChKK47yGkdS3HBIZfo91QrJwKpfATYziBjEnqipkTu+6jFylBIkxKTPye4b3vgcodZP8LSNVXAsMGTPNPJxzPWQ37ba4zMnYZ5iUerlaox/SNsn68DT6RajIb1A1JDq+HNFc3hQP2bzk2y5pCax8zo5swjdklnm4clfB2Lw==</X509Certificate>
</X509Data>
</KeyInfo>
</KeyDescriptor>
</AttributeAuthorityDescriptor>
</EntityDescriptor>`)
)
func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) { func TestCommandSide_AddInstanceGenericOAuthIDP(t *testing.T) {
type fields struct { type fields struct {
eventstore func(*testing.T) *eventstore.Eventstore eventstore func(*testing.T) *eventstore.Eventstore
@ -5180,7 +5247,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
} }
type args struct { type args struct {
ctx context.Context ctx context.Context
provider SAMLProvider provider *SAMLProvider
} }
type res struct { type res struct {
id string id string
@ -5201,7 +5268,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}, },
args{ args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5210,14 +5277,14 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}, },
}, },
{ {
"invalid metadata", "no metadata",
fields{ fields{
eventstore: expectEventstore(), eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
}, },
args{ args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
}, },
}, },
@ -5227,6 +5294,25 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}, },
}, },
}, },
{
"invalid metadata, error",
fields{
eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-SF3rwhgh", "Errors.Project.App.SAMLMetadataFormat"))
},
},
},
{ {
name: "ok", name: "ok",
fields: fields{ fields: fields{
@ -5236,7 +5322,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5258,9 +5344,9 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}, },
args: args{ args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5277,7 +5363,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5304,9 +5390,9 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}, },
args: args{ args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
Binding: "binding", Binding: "binding",
WithSignedRequest: true, WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
@ -5356,7 +5442,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
id string id string
provider SAMLProvider provider *SAMLProvider
} }
type res struct { type res struct {
want *domain.ObjectDetails want *domain.ObjectDetails
@ -5375,7 +5461,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
}, },
args{ args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5391,7 +5477,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args{ args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1", id: "id1",
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5400,14 +5486,14 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
}, },
}, },
{ {
"invalid metadata", "no metadata",
fields{ fields{
eventstore: expectEventstore(), eventstore: expectEventstore(),
}, },
args{ args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
}, },
}, },
@ -5417,6 +5503,25 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
}, },
}, },
}, },
{
"invalid metadata, error",
fields{
eventstore: expectEventstore(),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-dsfj3kl2", "Errors.Project.App.SAMLMetadataFormat"))
},
},
},
{ {
name: "not found", name: "not found",
fields: fields{ fields: fields{
@ -5427,9 +5532,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{ args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5445,7 +5550,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate, instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5465,9 +5570,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{ args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5505,7 +5610,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
"id1", "id1",
[]idp.SAMLIDPChanges{ []idp.SAMLIDPChanges{
idp.ChangeSAMLName("new name"), idp.ChangeSAMLName("new name"),
idp.ChangeSAMLMetadata([]byte("new metadata")), idp.ChangeSAMLMetadata(validSAMLMetadata),
idp.ChangeSAMLBinding("new binding"), idp.ChangeSAMLBinding("new binding"),
idp.ChangeSAMLWithSignedRequest(true), idp.ChangeSAMLWithSignedRequest(true),
idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)), idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)),
@ -5527,9 +5632,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{ args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"), ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "new name", Name: "new name",
Metadata: []byte("new metadata"), Metadata: validSAMLMetadata,
Binding: "new binding", Binding: "new binding",
WithSignedRequest: true, WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),

View File

@ -10,6 +10,7 @@ import (
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/idp/providers/saml"
"github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
@ -446,9 +447,9 @@ func (c *Commands) UpdateOrgLDAPProvider(ctx context.Context, resourceOwner, id
return pushedEventsToObjectDetails(pushedEvents), nil return pushedEventsToObjectDetails(pushedEvents), nil
} }
func (c *Commands) AddOrgSAMLProvider(ctx context.Context, resourceOwner string, provider SAMLProvider) (string, *domain.ObjectDetails, error) { func (c *Commands) AddOrgSAMLProvider(ctx context.Context, resourceOwner string, provider *SAMLProvider) (id string, details *domain.ObjectDetails, err error) {
orgAgg := org.NewAggregate(resourceOwner) orgAgg := org.NewAggregate(resourceOwner)
id, err := c.idGenerator.Next() id, err = c.idGenerator.Next()
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -464,7 +465,7 @@ func (c *Commands) AddOrgSAMLProvider(ctx context.Context, resourceOwner string,
return id, pushedEventsToObjectDetails(pushedEvents), nil return id, pushedEventsToObjectDetails(pushedEvents), nil
} }
func (c *Commands) UpdateOrgSAMLProvider(ctx context.Context, resourceOwner, id string, provider SAMLProvider) (*domain.ObjectDetails, error) { func (c *Commands) UpdateOrgSAMLProvider(ctx context.Context, resourceOwner, id string, provider *SAMLProvider) (details *domain.ObjectDetails, err error) {
orgAgg := org.NewAggregate(resourceOwner) orgAgg := org.NewAggregate(resourceOwner)
writeModel := NewSAMLOrgIDPWriteModel(resourceOwner, id) writeModel := NewSAMLOrgIDPWriteModel(resourceOwner, id)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgSAMLProvider(orgAgg, writeModel, provider)) cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgSAMLProvider(orgAgg, writeModel, provider))
@ -1703,7 +1704,7 @@ func (c *Commands) prepareUpdateOrgAppleProvider(a *org.Aggregate, writeModel *O
} }
} }
func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSAMLIDPWriteModel, provider SAMLProvider) preparation.Validation { func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSAMLIDPWriteModel, provider *SAMLProvider) preparation.Validation {
return func() (preparation.CreateCommands, error) { return func() (preparation.CreateCommands, error) {
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" { if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-957lr0f8u3", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "ORG-957lr0f8u3", "Errors.Invalid.Argument")
@ -1718,6 +1719,9 @@ func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSA
} }
provider.Metadata = data provider.Metadata = data
} }
if _, err := saml.ParseMetadata(provider.Metadata); err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "ORG-SF3rwhgh", "Errors.Project.App.SAMLMetadataFormat")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
events, err := filter(ctx, writeModel.Query()) events, err := filter(ctx, writeModel.Query())
if err != nil { if err != nil {
@ -1755,7 +1759,7 @@ func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSA
} }
} }
func (c *Commands) prepareUpdateOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSAMLIDPWriteModel, provider SAMLProvider) preparation.Validation { func (c *Commands) prepareUpdateOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSAMLIDPWriteModel, provider *SAMLProvider) preparation.Validation {
return func() (preparation.CreateCommands, error) { return func() (preparation.CreateCommands, error) {
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" { if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-wwdwdlaya0", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "ORG-wwdwdlaya0", "Errors.Invalid.Argument")
@ -1773,6 +1777,9 @@ func (c *Commands) prepareUpdateOrgSAMLProvider(a *org.Aggregate, writeModel *Or
if provider.Metadata == nil { if provider.Metadata == nil {
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-j6spncd74m", "Errors.Invalid.Argument") return nil, zerrors.ThrowInvalidArgument(nil, "ORG-j6spncd74m", "Errors.Invalid.Argument")
} }
if _, err := saml.ParseMetadata(provider.Metadata); err != nil {
return nil, zerrors.ThrowInvalidArgument(err, "ORG-SFqqh42", "Errors.Project.App.SAMLMetadataFormat")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
events, err := filter(ctx, writeModel.Query()) events, err := filter(ctx, writeModel.Query())
if err != nil { if err != nil {

View File

@ -5348,7 +5348,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
type args struct { type args struct {
ctx context.Context ctx context.Context
resourceOwner string resourceOwner string
provider SAMLProvider provider *SAMLProvider
} }
type res struct { type res struct {
id string id string
@ -5370,7 +5370,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args{ args{
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5379,7 +5379,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
}, },
}, },
{ {
"invalid metadata", "no metadata",
fields{ fields{
eventstore: expectEventstore(), eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@ -5387,7 +5387,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args{ args{
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
}, },
}, },
@ -5397,6 +5397,26 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
}, },
}, },
}, },
{
"invalid metadata, fail on error",
fields{
eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: context.Background(),
resourceOwner: "org1",
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-SF3rwhgh", "Errors.Project.App.SAMLMetadataFormat"))
},
},
},
{ {
name: "ok", name: "ok",
fields: fields{ fields: fields{
@ -5406,7 +5426,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5428,9 +5448,9 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5447,7 +5467,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5475,9 +5495,9 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args: args{ args: args{
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
Binding: "binding", Binding: "binding",
WithSignedRequest: true, WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
@ -5528,7 +5548,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx context.Context ctx context.Context
resourceOwner string resourceOwner string
id string id string
provider SAMLProvider provider *SAMLProvider
} }
type res struct { type res struct {
want *domain.ObjectDetails want *domain.ObjectDetails
@ -5548,7 +5568,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
args{ args{
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5565,7 +5585,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
id: "id1", id: "id1",
provider: SAMLProvider{}, provider: &SAMLProvider{},
}, },
res{ res{
err: func(err error) bool { err: func(err error) bool {
@ -5574,7 +5594,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
}, },
}, },
{ {
"invalid metadata", "no metadata",
fields{ fields{
eventstore: expectEventstore(), eventstore: expectEventstore(),
}, },
@ -5582,7 +5602,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
}, },
}, },
@ -5592,6 +5612,26 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
}, },
}, },
}, },
{
"invalid metadata, error",
fields{
eventstore: expectEventstore(),
},
args{
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "ORG-SFqqh42", "Errors.Project.App.SAMLMetadataFormat"))
},
},
},
{ {
name: "not found", name: "not found",
fields: fields{ fields: fields{
@ -5603,9 +5643,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5623,7 +5663,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1", "id1",
"name", "name",
[]byte("metadata"), validSAMLMetadata,
&crypto.CryptoValue{ &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption, CryptoType: crypto.TypeEncryption,
Algorithm: "enc", Algorithm: "enc",
@ -5644,9 +5684,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "name", Name: "name",
Metadata: []byte("metadata"), Metadata: validSAMLMetadata,
}, },
}, },
res: res{ res: res{
@ -5684,7 +5724,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
"id1", "id1",
[]idp.SAMLIDPChanges{ []idp.SAMLIDPChanges{
idp.ChangeSAMLName("new name"), idp.ChangeSAMLName("new name"),
idp.ChangeSAMLMetadata([]byte("new metadata")), idp.ChangeSAMLMetadata(validSAMLMetadata),
idp.ChangeSAMLBinding("new binding"), idp.ChangeSAMLBinding("new binding"),
idp.ChangeSAMLWithSignedRequest(true), idp.ChangeSAMLWithSignedRequest(true),
idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)), idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)),
@ -5707,9 +5747,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(), ctx: context.Background(),
resourceOwner: "org1", resourceOwner: "org1",
id: "id1", id: "id1",
provider: SAMLProvider{ provider: &SAMLProvider{
Name: "new name", Name: "new name",
Metadata: []byte("new metadata"), Metadata: validSAMLMetadata,
Binding: "new binding", Binding: "new binding",
WithSignedRequest: true, WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),

View File

@ -7,7 +7,6 @@ import (
"github.com/pquerna/otp" "github.com/pquerna/otp"
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http" http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
@ -79,10 +78,8 @@ func (c *Commands) createHumanTOTP(ctx context.Context, userID, resourceOwner st
logging.WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname") logging.WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get human for loginname")
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-SqyJz", "Errors.User.NotFound") return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-SqyJz", "Errors.User.NotFound")
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUserCredentials(ctx, human.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, human.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
org, err := c.getOrg(ctx, human.ResourceOwner) org, err := c.getOrg(ctx, human.ResourceOwner)
if err != nil { if err != nil {
@ -139,10 +136,8 @@ func (c *Commands) HumanCheckMFATOTPSetup(ctx context.Context, userID, code, use
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUserCredentials(ctx, existingOTP.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, existingOTP.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if existingOTP.State == domain.MFAStateUnspecified || existingOTP.State == domain.MFAStateRemoved { if existingOTP.State == domain.MFAStateUnspecified || existingOTP.State == domain.MFAStateRemoved {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotExisting") return nil, zerrors.ThrowNotFound(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotExisting")
@ -242,10 +237,8 @@ func (c *Commands) HumanRemoveTOTP(ctx context.Context, userID, resourceOwner st
if existingOTP.State == domain.MFAStateUnspecified || existingOTP.State == domain.MFAStateRemoved { if existingOTP.State == domain.MFAStateUnspecified || existingOTP.State == domain.MFAStateRemoved {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Hd9sd", "Errors.User.MFA.OTP.NotExisting") return nil, zerrors.ThrowNotFound(nil, "COMMAND-Hd9sd", "Errors.User.MFA.OTP.NotExisting")
} }
if userID != authz.GetCtxData(ctx).UserID { if err := c.checkPermissionUpdateUser(ctx, existingOTP.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel) userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanOTPRemovedEvent(ctx, userAgg)) pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanOTPRemovedEvent(ctx, userAgg))
@ -286,10 +279,8 @@ func (c *Commands) addHumanOTPSMS(ctx context.Context, userID, resourceOwner str
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUserCredentials(ctx, otpWriteModel.ResourceOwner(), userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, otpWriteModel.ResourceOwner(), userID); err != nil { return nil, err
return nil, err
}
} }
if otpWriteModel.otpAdded { if otpWriteModel.otpAdded {
return nil, zerrors.ThrowAlreadyExists(nil, "COMMAND-Ad3g2", "Errors.User.MFA.OTP.AlreadyReady") return nil, zerrors.ThrowAlreadyExists(nil, "COMMAND-Ad3g2", "Errors.User.MFA.OTP.AlreadyReady")
@ -318,10 +309,8 @@ func (c *Commands) RemoveHumanOTPSMS(ctx context.Context, userID, resourceOwner
if err != nil { if err != nil {
return nil, err return nil, err
} }
if userID != authz.GetCtxData(ctx).UserID { if err := c.checkPermissionUpdateUser(ctx, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.WriteModel.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if !existingOTP.otpAdded { if !existingOTP.otpAdded {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Sr3h3", "Errors.User.MFA.OTP.NotExisting") return nil, zerrors.ThrowNotFound(nil, "COMMAND-Sr3h3", "Errors.User.MFA.OTP.NotExisting")
@ -420,10 +409,8 @@ func (c *Commands) addHumanOTPEmail(ctx context.Context, userID, resourceOwner s
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUserCredentials(ctx, otpWriteModel.ResourceOwner(), userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, otpWriteModel.ResourceOwner(), userID); err != nil { return nil, err
return nil, err
}
} }
if otpWriteModel.otpAdded { if otpWriteModel.otpAdded {
return nil, zerrors.ThrowAlreadyExists(nil, "COMMAND-MKL2s", "Errors.User.MFA.OTP.AlreadyReady") return nil, zerrors.ThrowAlreadyExists(nil, "COMMAND-MKL2s", "Errors.User.MFA.OTP.AlreadyReady")
@ -452,10 +439,8 @@ func (c *Commands) RemoveHumanOTPEmail(ctx context.Context, userID, resourceOwne
if err != nil { if err != nil {
return nil, err return nil, err
} }
if userID != authz.GetCtxData(ctx).UserID { if err := c.checkPermissionUpdateUser(ctx, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.WriteModel.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if !existingOTP.otpAdded { if !existingOTP.otpAdded {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-b312D", "Errors.User.MFA.OTP.NotExisting") return nil, zerrors.ThrowNotFound(nil, "COMMAND-b312D", "Errors.User.MFA.OTP.NotExisting")

View File

@ -110,7 +110,7 @@ type setPasswordVerification func(ctx context.Context) (newEncodedPassword strin
// setPasswordWithPermission returns a permission check as [setPasswordVerification] implementation // setPasswordWithPermission returns a permission check as [setPasswordVerification] implementation
func (c *Commands) setPasswordWithPermission(userID, orgID string) setPasswordVerification { func (c *Commands) setPasswordWithPermission(userID, orgID string) setPasswordVerification {
return func(ctx context.Context) (_ string, err error) { return func(ctx context.Context) (_ string, err error) {
return "", c.checkPermission(ctx, domain.PermissionUserWrite, orgID, userID) return "", c.checkPermissionUpdateUser(ctx, orgID, userID)
} }
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@ -146,10 +145,8 @@ func (c *Commands) addHumanWebAuthN(ctx context.Context, userID, resourceowner,
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUserCredentials(ctx, user.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserCredentialWrite, user.ResourceOwner, userID); err != nil { return nil, nil, nil, err
return nil, nil, nil, err
}
} }
org, err := c.getOrg(ctx, user.ResourceOwner) org, err := c.getOrg(ctx, user.ResourceOwner)
if err != nil { if err != nil {
@ -603,10 +600,9 @@ func (c *Commands) removeHumanWebAuthN(ctx context.Context, userID, webAuthNID,
if existingWebAuthN.State == domain.MFAStateUnspecified || existingWebAuthN.State == domain.MFAStateRemoved { if existingWebAuthN.State == domain.MFAStateUnspecified || existingWebAuthN.State == domain.MFAStateRemoved {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-DAfb2", "Errors.User.WebAuthN.NotFound") return nil, zerrors.ThrowNotFound(nil, "COMMAND-DAfb2", "Errors.User.WebAuthN.NotFound")
} }
if userID != authz.GetCtxData(ctx).UserID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingWebAuthN.ResourceOwner, existingWebAuthN.AggregateID); err != nil { if err := c.checkPermissionUpdateUser(ctx, existingWebAuthN.ResourceOwner, existingWebAuthN.AggregateID); err != nil {
return nil, err return nil, err
}
} }
userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel) userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel)

View File

@ -127,6 +127,16 @@ func (c *Commands) checkPermissionUpdateUser(ctx context.Context, resourceOwner,
return nil return nil
} }
func (c *Commands) checkPermissionUpdateUserCredentials(ctx context.Context, resourceOwner, userID string) error {
if userID != "" && userID == authz.GetCtxData(ctx).UserID {
return nil
}
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, resourceOwner, userID); err != nil {
return err
}
return nil
}
func (c *Commands) checkPermissionDeleteUser(ctx context.Context, resourceOwner, userID string) error { func (c *Commands) checkPermissionDeleteUser(ctx context.Context, resourceOwner, userID string) error {
if userID != "" && userID == authz.GetCtxData(ctx).UserID { if userID != "" && userID == authz.GetCtxData(ctx).UserID {
return nil return nil

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@ -118,10 +117,8 @@ func (c *Commands) changeUserEmailWithGeneratorEvents(ctx context.Context, userI
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if err = cmd.Change(ctx, domain.EmailAddress(email)); err != nil { if err = cmd.Change(ctx, domain.EmailAddress(email)); err != nil {
return nil, err return nil, err
@ -137,10 +134,8 @@ func (c *Commands) resendUserEmailCodeWithGeneratorEvents(ctx context.Context, u
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if cmd.model.Code == nil { if cmd.model.Code == nil {
return nil, zerrors.ThrowPreconditionFailed(err, "EMAIL-5w5ilin4yt", "Errors.User.Code.Empty") return nil, zerrors.ThrowPreconditionFailed(err, "EMAIL-5w5ilin4yt", "Errors.User.Code.Empty")

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
@ -74,10 +73,8 @@ func (c *Commands) ResendInviteCode(ctx context.Context, userID, resourceOwner,
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err := c.checkPermissionUpdateUser(ctx, existingCode.ResourceOwner, userID); err != nil {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingCode.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if !existingCode.UserState.Exists() { if !existingCode.UserState.Exists() {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-H3b2a", "Errors.User.NotFound") return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-H3b2a", "Errors.User.NotFound")

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
@ -18,7 +17,7 @@ import (
// RegisterUserPasskey creates a passkey registration for the current authenticated user. // RegisterUserPasskey creates a passkey registration for the current authenticated user.
// UserID, usually taken from the request is compared against the user ID in the context. // UserID, usually taken from the request is compared against the user ID in the context.
func (c *Commands) RegisterUserPasskey(ctx context.Context, userID, resourceOwner, rpID string, authenticator domain.AuthenticatorAttachment) (*domain.WebAuthNRegistrationDetails, error) { func (c *Commands) RegisterUserPasskey(ctx context.Context, userID, resourceOwner, rpID string, authenticator domain.AuthenticatorAttachment) (*domain.WebAuthNRegistrationDetails, error) {
if err := authz.UserIDInCTX(ctx, userID); err != nil { if err := c.checkPermissionUpdateUserCredentials(ctx, resourceOwner, userID); err != nil {
return nil, err return nil, err
} }
return c.registerUserPasskey(ctx, userID, resourceOwner, rpID, authenticator) return c.registerUserPasskey(ctx, userID, resourceOwner, rpID, authenticator)

View File

@ -34,8 +34,9 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
} }
userAgg := &user.NewAggregate("user1", "org1").Aggregate userAgg := &user.NewAggregate("user1", "org1").Aggregate
type fields struct { type fields struct {
eventstore *eventstore.Eventstore eventstore func(t *testing.T) *eventstore.Eventstore
idGenerator id.Generator idGenerator id.Generator
checkPermission domain.PermissionCheck
} }
type args struct { type args struct {
userID string userID string
@ -51,18 +52,22 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
wantErr error wantErr error
}{ }{
{ {
name: "wrong user", name: "no permission",
fields: fields{
eventstore: expectEventstore(),
checkPermission: newMockPermissionCheckNotAllowed(),
},
args: args{ args: args{
userID: "foo", userID: "foo",
resourceOwner: "org1", resourceOwner: "org1",
authenticator: domain.AuthenticatorAttachmentCrossPlattform, authenticator: domain.AuthenticatorAttachmentCrossPlattform,
}, },
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong"), wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
}, },
{ {
name: "get human passwordless error", name: "get human passwordless error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilterError(io.ErrClosedPipe), expectFilterError(io.ErrClosedPipe),
), ),
}, },
@ -76,7 +81,7 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
{ {
name: "id generator error", name: "id generator error",
fields: fields{ fields: fields{
eventstore: eventstoreExpect(t, eventstore: expectEventstore(
expectFilter(), // getHumanPasswordlessTokens expectFilter(), // getHumanPasswordlessTokens
expectFilter(eventFromEventPusher( expectFilter(eventFromEventPusher(
user.NewHumanAddedEvent(ctx, user.NewHumanAddedEvent(ctx,
@ -118,9 +123,10 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := &Commands{ c := &Commands{
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator, idGenerator: tt.fields.idGenerator,
webauthnConfig: webauthnConfig, webauthnConfig: webauthnConfig,
checkPermission: tt.fields.checkPermission,
} }
_, err := c.RegisterUserPasskey(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.rpID, tt.args.authenticator) _, err := c.RegisterUserPasskey(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.rpID, tt.args.authenticator)
require.ErrorIs(t, err, tt.wantErr) require.ErrorIs(t, err, tt.wantErr)

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"io" "io"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/repository/user"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
@ -50,10 +49,8 @@ func (c *Commands) requestPasswordReset(ctx context.Context, userID string, retu
if model.UserState == domain.UserStateInitial { if model.UserState == domain.UserStateInitial {
return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Sfe4g", "Errors.User.NotInitialised") return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Sfe4g", "Errors.User.NotInitialised")
} }
if authz.GetCtxData(ctx).UserID != userID { if err = c.checkPermissionUpdateUser(ctx, model.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, model.ResourceOwner, userID); err != nil { return nil, nil, err
return nil, nil, err
}
} }
var passwordCode *EncryptedCode var passwordCode *EncryptedCode
var generatorID string var generatorID string

View File

@ -82,10 +82,8 @@ func (c *Commands) changeUserPhoneWithGenerator(ctx context.Context, userID, pho
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if err = cmd.Change(ctx, domain.PhoneNumber(phone)); err != nil { if err = cmd.Change(ctx, domain.PhoneNumber(phone)); err != nil {
return nil, err return nil, err
@ -104,10 +102,8 @@ func (c *Commands) resendUserPhoneCodeWithGenerator(ctx context.Context, userID
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authz.GetCtxData(ctx).UserID != userID { if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil { return nil, err
return nil, err
}
} }
if cmd.model.Code == nil && cmd.model.GeneratorID == "" { if cmd.model.Code == nil && cmd.model.GeneratorID == "" {
return nil, zerrors.ThrowPreconditionFailed(err, "PHONE-5xrra88eq8", "Errors.User.Code.Empty") return nil, zerrors.ThrowPreconditionFailed(err, "PHONE-5xrra88eq8", "Errors.User.Code.Empty")

View File

@ -1,15 +1,19 @@
package saml package saml
import ( import (
"bytes"
"context" "context"
"crypto/rsa" "crypto/rsa"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/xml" "encoding/xml"
"io"
"net/url" "net/url"
"time"
"github.com/crewjam/saml" "github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp" "github.com/crewjam/saml/samlsp"
"golang.org/x/text/encoding/ianaindex"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp"
@ -104,6 +108,41 @@ func WithEntityID(entityID string) ProviderOpts {
} }
} }
// ParseMetadata parses the metadata with the provided XML encoding and returns the EntityDescriptor
func ParseMetadata(metadata []byte) (*saml.EntityDescriptor, error) {
entityDescriptor := new(saml.EntityDescriptor)
reader := bytes.NewReader(metadata)
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = func(charset string, reader io.Reader) (io.Reader, error) {
enc, err := ianaindex.IANA.Encoding(charset)
if err != nil {
return nil, err
}
return enc.NewDecoder().Reader(reader), nil
}
if err := decoder.Decode(entityDescriptor); err != nil {
if err.Error() == "expected element type <EntityDescriptor> but have <EntitiesDescriptor>" {
// reset reader to start of metadata so we can try to parse it as an EntitiesDescriptor
if _, err := reader.Seek(0, io.SeekStart); err != nil {
return nil, err
}
entities := &saml.EntitiesDescriptor{}
if err := decoder.Decode(entities); err != nil {
return nil, err
}
for i, e := range entities.EntityDescriptors {
if len(e.IDPSSODescriptors) > 0 {
return &entities.EntityDescriptors[i], nil
}
}
return nil, zerrors.ThrowInternal(nil, "SAML-Ejoi3r2", "no entity found with IDPSSODescriptor")
}
return nil, err
}
return entityDescriptor, nil
}
func New( func New(
name string, name string,
rootURLStr string, rootURLStr string,
@ -112,8 +151,8 @@ func New(
key []byte, key []byte,
options ...ProviderOpts, options ...ProviderOpts,
) (*Provider, error) { ) (*Provider, error) {
entityDescriptor := new(saml.EntityDescriptor) entityDescriptor, err := ParseMetadata(metadata)
if err := xml.Unmarshal(metadata, entityDescriptor); err != nil { if err != nil {
return nil, err return nil, err
} }
keyPair, err := tls.X509KeyPair(certificate, key) keyPair, err := tls.X509KeyPair(certificate, key)
@ -180,6 +219,7 @@ func (p *Provider) GetSP() (*samlsp.Middleware, error) {
if p.binding != "" { if p.binding != "" {
sp.Binding = p.binding sp.Binding = p.binding
} }
sp.ServiceProvider.MetadataValidDuration = time.Until(sp.ServiceProvider.Certificate.NotAfter)
return sp, nil return sp, nil
} }

View File

@ -1,6 +1,7 @@
package saml package saml
import ( import (
"encoding/xml"
"testing" "testing"
"github.com/crewjam/saml" "github.com/crewjam/saml"
@ -10,6 +11,7 @@ import (
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/idp/providers/saml/requesttracker" "github.com/zitadel/zitadel/internal/idp/providers/saml/requesttracker"
"github.com/zitadel/zitadel/internal/zerrors"
) )
func TestProvider_Options(t *testing.T) { func TestProvider_Options(t *testing.T) {
@ -170,3 +172,111 @@ func TestProvider_Options(t *testing.T) {
}) })
} }
} }
func TestParseMetadata(t *testing.T) {
type args struct {
metadata []byte
}
tests := []struct {
name string
args args
want *saml.EntityDescriptor
wantErr error
}{
{
"invalid",
args{
metadata: []byte(`<Test></Test>`),
},
nil,
xml.UnmarshalError("expected element type <EntityDescriptor> but have <Test>"),
},
{
"valid entity descriptor",
args{
metadata: []byte(`<?xml version="1.0" encoding="UTF-8"?><EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/metadata"><IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/sso"></SingleSignOnService></IDPSSODescriptor></EntityDescriptor>`),
},
&saml.EntityDescriptor{
EntityID: "http://localhost:8000/metadata",
IDPSSODescriptors: []saml.IDPSSODescriptor{
{
XMLName: xml.Name{
Space: "urn:oasis:names:tc:SAML:2.0:metadata",
Local: "IDPSSODescriptor",
},
SingleSignOnServices: []saml.Endpoint{
{
Binding: saml.HTTPRedirectBinding,
Location: "http://localhost:8000/sso",
},
},
},
},
},
nil,
},
{
"valid entity descriptor, non utf-8",
args{
metadata: []byte(`<?xml version="1.0" encoding="windows-1252"?><EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/metadata"><IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/sso"></SingleSignOnService></IDPSSODescriptor></EntityDescriptor>`),
},
&saml.EntityDescriptor{
EntityID: "http://localhost:8000/metadata",
IDPSSODescriptors: []saml.IDPSSODescriptor{
{
XMLName: xml.Name{
Space: "urn:oasis:names:tc:SAML:2.0:metadata",
Local: "IDPSSODescriptor",
},
SingleSignOnServices: []saml.Endpoint{
{
Binding: saml.HTTPRedirectBinding,
Location: "http://localhost:8000/sso",
},
},
},
},
},
nil,
},
{
"entities descriptor without IDPSSODescriptor",
args{
metadata: []byte(`<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/metadata"></EntityDescriptor></EntitiesDescriptor>`),
},
nil,
zerrors.ThrowInternal(nil, "SAML-Ejoi3r2", "no entity found with IDPSSODescriptor"),
},
{
"valid entities descriptor",
args{
metadata: []byte(`<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/metadata"><IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/sso"></SingleSignOnService></IDPSSODescriptor></EntityDescriptor></EntitiesDescriptor>`),
},
&saml.EntityDescriptor{
EntityID: "http://localhost:8000/metadata",
IDPSSODescriptors: []saml.IDPSSODescriptor{
{
XMLName: xml.Name{
Space: "urn:oasis:names:tc:SAML:2.0:metadata",
Local: "IDPSSODescriptor",
},
SingleSignOnServices: []saml.Endpoint{
{
Binding: saml.HTTPRedirectBinding,
Location: "http://localhost:8000/sso",
},
},
},
},
},
nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseMetadata(tt.args.metadata)
assert.ErrorIs(t, err, tt.wantErr)
assert.Equal(t, tt.want, got)
})
}
}

View File

@ -9,7 +9,6 @@ import (
"github.com/crewjam/saml" "github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp" "github.com/crewjam/saml/samlsp"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/idp" "github.com/zitadel/zitadel/internal/idp"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
@ -71,7 +70,7 @@ func (s *Session) FetchUser(ctx context.Context) (user idp.User, err error) {
if err != nil { if err != nil {
invalidRespErr := new(saml.InvalidResponseError) invalidRespErr := new(saml.InvalidResponseError)
if errors.As(err, &invalidRespErr) { if errors.As(err, &invalidRespErr) {
logging.WithError(invalidRespErr.PrivateErr).Info("invalid SAML response details") return nil, zerrors.ThrowInvalidArgument(invalidRespErr.PrivateErr, "SAML-ajl3irfs", "Errors.Intent.ResponseInvalid")
} }
return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid") return nil, zerrors.ThrowInvalidArgument(err, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid")
} }

View File

@ -134,7 +134,7 @@ func TestSession_FetchUser(t *testing.T) {
requestID: "id-b22c90db88bf01d82ffb0a7b6fe25ac9fcb2c679", requestID: "id-b22c90db88bf01d82ffb0a7b6fe25ac9fcb2c679",
}, },
want: want{ want: want{
err: zerrors.ThrowInvalidArgument(nil, "SAML-nuo0vphhh9", "Errors.Intent.ResponseInvalid"), err: zerrors.ThrowInvalidArgument(nil, "SAML-ajl3irfs", "Errors.Intent.ResponseInvalid"),
}, },
}, },
{ {

View File

@ -0,0 +1,68 @@
InitCode:
Title: 사용자 초기화
PreHeader: 사용자 초기화
Subject: 사용자 초기화
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 사용자가 생성되었습니다. 로그인하려면 사용자 이름 {{.PreferredLoginName}}을 사용하세요. 초기화 프로세스를 완료하려면 아래 버튼을 클릭하세요. (코드 {{.Code}}) 이 메일을 요청하지 않으셨다면 무시하셔도 됩니다.
ButtonText: 초기화 완료
PasswordReset:
Title: 비밀번호 재설정
PreHeader: 비밀번호 재설정
Subject: 비밀번호 재설정
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 비밀번호 재설정 요청을 받았습니다. 비밀번호를 재설정하려면 아래 버튼을 사용하세요. (코드 {{.Code}}) 이 메일을 요청하지 않으셨다면 무시하셔도 됩니다.
ButtonText: 비밀번호 재설정
VerifyEmail:
Title: 이메일 인증
PreHeader: 이메일 인증
Subject: 이메일 인증
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 새 이메일이 추가되었습니다. 이메일을 인증하려면 아래 버튼을 사용하세요. (코드 {{.Code}}) 새로운 이메일을 추가하지 않으셨다면 이 메일을 무시하세요.
ButtonText: 이메일 인증
VerifyPhone:
Title: 전화번호 인증
PreHeader: 전화번호 인증
Subject: 전화번호 인증
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 새 전화번호가 추가되었습니다. 다음 코드를 사용하여 인증하세요 {{.Code}}
ButtonText: 전화번호 인증
VerifyEmailOTP:
Title: 일회용 비밀번호 인증
PreHeader: 일회용 비밀번호 인증
Subject: 일회용 비밀번호 인증
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 인증하려면 다음 일회용 비밀번호 {{.OTP}}를 5분 이내에 사용하거나 "인증" 버튼을 클릭하세요.
ButtonText: 인증
VerifySMSOTP:
Text: >-
{{.OTP}}는 {{ .Domain }}의 일회용 비밀번호입니다. 다음 {{.Expiry}} 이내에 사용하세요.
@{{.Domain}} #{{.OTP}}
DomainClaimed:
Title: 조직에서 도메인을 소유하게 되었습니다
PreHeader: 이메일 / 사용자 이름 변경
Subject: 조직에서 도메인을 소유하게 되었습니다
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 도메인 {{.Domain}} 은(는) 조직에서 소유하게 되었습니다. 현재 사용자인 {{.Username}}은 이 조직의 구성원이 아닙니다. 따라서 로그인할 때 이메일을 변경해야 합니다. 이 로그인용으로 임시 사용자 이름 ({{.TempUsername}})을 생성했습니다.
ButtonText: 로그인
PasswordlessRegistration:
Title: 비밀번호 없는 로그인 추가
PreHeader: 비밀번호 없는 로그인 추가
Subject: 비밀번호 없는 로그인 추가
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 비밀번호 없는 로그인을 위한 토큰 추가 요청을 받았습니다. 비밀번호 없는 로그인을 위해 토큰 또는 장치를 추가하려면 아래 버튼을 클릭하세요.
ButtonText: 비밀번호 없는 로그인 추가
PasswordChange:
Title: 사용자 비밀번호 변경됨
PreHeader: 비밀번호 변경
Subject: 사용자 비밀번호 변경됨
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: 사용자의 비밀번호가 변경되었습니다. 본인이 직접 변경하지 않으셨다면 즉시 비밀번호를 재설정하시기 바랍니다.
ButtonText: 로그인
InviteUser:
Title: "{{.ApplicationName}} 초대"
PreHeader: "{{.ApplicationName}} 초대"
Subject: "{{.ApplicationName}} 초대"
Greeting: 안녕하세요, {{.DisplayName}}님,
Text: "{{.ApplicationName}}에 초대되었습니다. 초대 프로세스를 완료하려면 아래 버튼을 클릭하세요. 이 메일을 요청하지 않으셨다면 무시하셔도 됩니다."
ButtonText: 초대 수락

1406
internal/static/i18n/ko.yaml Normal file

File diff suppressed because it is too large Load Diff