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 ;-) |
| 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 |
| 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 localeSv from '@angular/common/locales/sv';
import localeHu from '@angular/common/locales/hu';
import localeKo from '@angular/common/locales/ko';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { MatNativeDateModule } from '@angular/material/core';
import { MatDialogModule } from '@angular/material/dialog';
@ -108,6 +109,8 @@ registerLocaleData(localeSv);
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/sv.json'));
registerLocaleData(localeHu);
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 {
getTranslation(lang: string): Observable<any> {

View File

@ -16,6 +16,7 @@ export const supportedLanguages = [
'nl',
'sv',
'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';

View File

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

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"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.",
"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.",
"FORCEMFA": "Vynutit MFA",
"FORCEMFALOCALONLY": "Vynutit MFA pouze pro lokálně ověřené uživatele",
"FORCEMFA": "Vynuti MFA pro všechny 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.",
"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",
@ -2572,7 +2574,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Přidat manažera",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "Email Verification erfolgreich",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Der konventionelle Login mit Benutzername und Passwort wird 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.",
"FORCEMFA": "MFA erzwingen",
"FORCEMFALOCALONLY": "MFA für lokale Benutzer erzwingen",
"FORCEMFA": "MFA für alle 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.",
"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",
@ -2563,7 +2565,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Manager hinzufügen",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "Email verification done",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "The conventional login with user name and password is allowed.",
"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.",
"FORCEMFA": "Force MFA",
"FORCEMFALOCALONLY": "Force MFA for local authenticated users",
"FORCEMFA": "Force MFA for all 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.",
"HIDEPASSWORDRESET_DESC": "If the option is selected, the user can't reset his password in the login process.",
"HIDELOGINNAMESUFFIX": "Hide Loginname suffix",
@ -2588,7 +2590,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Add a Manager",

View File

@ -1385,7 +1385,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1622,7 +1623,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"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.",
"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.",
"FORCEMFA": "Forzar MFA",
"FORCEMFALOCALONLY": "Forzar MFA para usuarios locales",
"FORCEMFA": "Forzar MFA para todos los usuarios",
"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",
"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",
@ -2560,7 +2562,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Añadir un Mánager",

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"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.",
"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.",
"FORCEMFA": "Forcer MFA",
"FORCEMFALOCALONLY": "Forcer MFA pour les utilisateurs locaux",
"FORCEMFA": "Forcer MFA pour tous les utilisateurs",
"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.",
"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",
@ -2564,7 +2566,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Ajouter un responsable",

View File

@ -1384,7 +1384,8 @@
"nl": "Holland",
"sv": "Svéd",
"id": "Indonéz",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1619,7 +1620,8 @@
"ru": "Orosz",
"nl": "Holland",
"sv": "Svéd",
"id": "Indonéz"
"id": "Indonéz",
"ko": "한국어"
},
"KEYS": {
"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.",
"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.",
"FORCEMFA": "MFA kényszerítése",
"FORCEMFALOCALONLY": "Kényszerítsd az MFA-t a helyileg hitelesített felhasználókra",
"FORCEMFA": "MFA kikényszerítése minden felhasználó számára",
"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.",
"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",

View File

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

View File

@ -1384,7 +1384,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1621,7 +1622,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "Verifica dell'e-mail terminata con successo.",
@ -1721,8 +1723,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "Autenticazione classica con nome utente e password è permessa.",
"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.",
"FORCEMFA": "Forza MFA",
"FORCEMFALOCALONLY": "Forza MFA per gli utenti locali",
"FORCEMFA": "Forzare MFA per tutti gli utenti",
"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.",
"HIDEPASSWORDRESET_DESC": "Se l'opzione è selezionata, l'utente non può resettare la sua password nel interfaccia login.",
"HIDELOGINNAMESUFFIX": "Nascondi il suffisso del nome utente",
@ -2564,7 +2566,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Aggiungi un manager",

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1620,7 +1620,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "E-mail verificatie voltooid",
@ -1720,8 +1721,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "De conventionele login met gebruikersnaam en wachtwoord is toegestaan.",
"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.",
"FORCEMFA": "Forceer MFA",
"FORCEMFALOCALONLY": "Forceer MFA voor lokaal geauthenticeerde gebruikers",
"FORCEMFA": "MFA afdwingen voor alle 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.",
"HIDEPASSWORDRESET_DESC": "Als de optie is geselecteerd, kan de gebruiker zijn wachtwoord niet resetten in het login proces.",
"HIDELOGINNAMESUFFIX": "Verberg Inlognaam achtervoegsel",
@ -2580,7 +2581,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Voeg een Manager toe",

View File

@ -1383,7 +1383,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1620,7 +1621,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"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.",
"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.",
"FORCEMFA": "Wymuś MFA",
"FORCEMFALOCALONLY": "Wymuś MFA dla lokalnych użytkowników",
"FORCEMFA": "Wymuś MFA dla wszystkich 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.",
"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",
@ -2563,7 +2565,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Dodaj managera",

View File

@ -1385,7 +1385,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1622,7 +1623,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "Verificação de email concluída",
@ -1722,7 +1724,8 @@
"ALLOWUSERNAMEPASSWORD_DESC": "O login convencional com nome de usuário e senha é permitido.",
"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.",
"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.",
"HIDELOGINNAMESUFFIX": "Ocultar sufixo do nome de login",
"HIDELOGINNAMESUFFIX_DESC": "Oculta o sufixo do nome de login na interface de login",
@ -2558,7 +2561,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Adicionar um Gerente",

View File

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

View File

@ -1388,7 +1388,8 @@
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia",
"hu": "Magyar"
"hu": "Magyar",
"ko": "한국어"
}
},
"SMTP": {
@ -1625,7 +1626,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"KEYS": {
"emailVerificationDoneText": "E-postverifiering klar",
@ -1725,8 +1727,8 @@
"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",
"ALLOWREGISTER_DESC": "Om alternativet är valt visas ett ytterligare steg för att registrera en användare i inloggningen.",
"FORCEMFA": "Tvinga MFA",
"FORCEMFALOCALONLY": "Tvinga MFA för lokalt autentiserade användare",
"FORCEMFA": "Tvinga MFA för alla 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.",
"HIDEPASSWORDRESET_DESC": "Om alternativet är valt kan användaren inte återställa sitt lösenord i inloggningsprocessen.",
"HIDELOGINNAMESUFFIX": "Dölj inloggningsnamn suffix",
@ -2592,7 +2594,8 @@
"ru": "Русский",
"nl": "Nederlands",
"sv": "Svenska",
"id": "Bahasa Indonesia"
"id": "Bahasa Indonesia",
"ko": "한국어"
},
"MEMBER": {
"ADD": "Lägg till en administratör",

View File

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

View File

@ -51,6 +51,7 @@ ZITADEL is available in the following languages
- Dutch (nl)
- Swedish (sv)
- Hungarian (hu)
- 한국어 (ko)
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.

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
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
return command.SAMLProvider{
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
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
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
return command.SAMLProvider{
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
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
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
return command.SAMLProvider{
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
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
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
return command.SAMLProvider{
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(),

View File

@ -58,7 +58,7 @@ func TestServer_AddOTPSMS(t *testing.T) {
wantErr: true,
},
{
name: "user mismatch",
name: "no permission",
args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPSMSRequest{
@ -127,14 +127,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Instance.Client.UserV2.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
_, err := Instance.Client.UserV2.VerifyPhone(CTX, &user.VerifyPhoneRequest{
UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetPhoneCode(),
})
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)
type args struct {
@ -157,10 +167,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
},
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",
args: args{
ctx: userVerifiedCtx,
ctx: CTX,
req: &user.RemoveOTPSMSRequest{
UserId: userVerified.GetUserId(),
},
@ -230,7 +254,7 @@ func TestServer_AddOTPEmail(t *testing.T) {
wantErr: true,
},
{
name: "user mismatch",
name: "no permission",
args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPEmailRequest{
@ -301,14 +325,24 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Instance.Client.UserV2.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
_, err := Instance.Client.UserV2.VerifyEmail(CTX, &user.VerifyEmailRequest{
UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetEmailCode(),
})
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)
type args struct {
@ -331,10 +365,25 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
},
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",
args: args{
ctx: userVerifiedCtx,
ctx: CTX,
req: &user.RemoveOTPEmailRequest{
UserId: userVerified.GetUserId(),
},

View File

@ -93,15 +93,30 @@ func TestServer_RegisterPasskey(t *testing.T) {
wantErr: true,
},
{
name: "user mismatch",
name: "user no permission",
args: args{
ctx: CTX,
ctx: UserCTX,
req: &user.RegisterPasskeyRequest{
UserId: userID,
},
},
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",
args: args{

View File

@ -13,7 +13,6 @@ func (s *Server) AddOTPSMS(ctx context.Context, req *user.AddOTPSMSRequest) (*us
return nil, err
}
return &user.AddOTPSMSResponse{Details: object.DomainToDetailsPb(details)}, nil
}
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,
},
{
name: "user mismatch",
name: "no permission",
args: args{
ctx: integration.WithAuthorizationToken(context.Background(), sessionTokenOtherUser),
req: &user.AddOTPSMSRequest{
@ -127,14 +127,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Client.VerifyPhone(userVerifiedCtx, &user.VerifyPhoneRequest{
_, err := Instance.Client.UserV2beta.VerifyPhone(CTX, &user.VerifyPhoneRequest{
UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetPhoneCode(),
})
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)
type args struct {
@ -157,10 +167,24 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
},
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",
args: args{
ctx: userVerifiedCtx,
ctx: CTX,
req: &user.RemoveOTPSMSRequest{
UserId: userVerified.GetUserId(),
},
@ -301,14 +325,24 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
userVerified := Instance.CreateHumanUser(CTX)
Instance.RegisterUserPasskey(CTX, userVerified.GetUserId())
_, sessionTokenVerified, _, _ := Instance.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
userVerifiedCtx := integration.WithAuthorizationToken(context.Background(), sessionTokenVerified)
_, err := Client.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
_, err := Client.VerifyEmail(CTX, &user.VerifyEmailRequest{
UserId: userVerified.GetUserId(),
VerificationCode: userVerified.GetEmailCode(),
})
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)
type args struct {
@ -331,10 +365,25 @@ func TestServer_RemoveOTPEmail(t *testing.T) {
},
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",
args: args{
ctx: userVerifiedCtx,
ctx: CTX,
req: &user.RemoveOTPEmailRequest{
UserId: userVerified.GetUserId(),
},

View File

@ -92,15 +92,30 @@ func TestServer_RegisterPasskey(t *testing.T) {
wantErr: true,
},
{
name: "user mismatch",
name: "user no permission",
args: args{
ctx: CTX,
ctx: UserCTX,
req: &user.RegisterPasskeyRequest{
UserId: userID,
},
},
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",
args: args{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -256,6 +256,7 @@ RegistrationUser:
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
GenderLabel: 性別
Female: 女性
Male: 男性
@ -298,6 +299,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: 利用規約
TosConfirm: 私は利用規約を承諾します。
TosLinkText: TOS
@ -374,6 +376,7 @@ ExternalNotFound:
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
DeviceAuth:
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
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
GenderLabel: Пол
Female: Женски
Male: Машки
@ -306,6 +307,7 @@ ExternalRegistrationUserOverview:
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
TosAndPrivacyLabel: Правила и услови
TosConfirm: Се согласувам со
TosLinkText: правилата за користење
@ -382,6 +384,7 @@ ExternalNotFound:
Swedish: Svenska
Indonesian: Bahasa Indonesia
Hungarian: Magyar
Korean: 한국어
DeviceAuth:
Title: Овластување преку уред

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,6 +11,7 @@ import (
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"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/zerrors"
)
@ -511,10 +512,10 @@ func (c *Commands) UpdateInstanceAppleProvider(ctx context.Context, id string, p
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()
instanceAgg := instance.NewAggregate(instanceID)
id, err := c.idGenerator.Next()
id, err = c.idGenerator.Next()
if err != nil {
return "", nil, err
}
@ -530,7 +531,7 @@ func (c *Commands) AddInstanceSAMLProvider(ctx context.Context, provider SAMLPro
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()
instanceAgg := instance.NewAggregate(instanceID)
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) {
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
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 {
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) {
events, err := filter(ctx, writeModel.Query())
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) {
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
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
}
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) {
events, err := filter(ctx, writeModel.Query())
if err != nil {

View File

@ -22,6 +22,73 @@ import (
"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) {
type fields struct {
eventstore func(*testing.T) *eventstore.Eventstore
@ -5180,7 +5247,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
}
type args struct {
ctx context.Context
provider SAMLProvider
provider *SAMLProvider
}
type res struct {
id string
@ -5201,7 +5268,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5210,14 +5277,14 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
},
},
{
"invalid metadata",
"no metadata",
fields{
eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{
provider: &SAMLProvider{
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",
fields: fields{
@ -5236,7 +5322,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5258,9 +5344,9 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5277,7 +5363,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5304,9 +5390,9 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
Binding: "binding",
WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
@ -5356,7 +5442,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
type args struct {
ctx context.Context
id string
provider SAMLProvider
provider *SAMLProvider
}
type res struct {
want *domain.ObjectDetails
@ -5375,7 +5461,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5391,7 +5477,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5400,14 +5486,14 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
},
},
{
"invalid metadata",
"no metadata",
fields{
eventstore: expectEventstore(),
},
args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
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",
fields: fields{
@ -5427,9 +5532,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5445,7 +5550,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
instance.NewSAMLIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5465,9 +5570,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5505,7 +5610,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
"id1",
[]idp.SAMLIDPChanges{
idp.ChangeSAMLName("new name"),
idp.ChangeSAMLMetadata([]byte("new metadata")),
idp.ChangeSAMLMetadata(validSAMLMetadata),
idp.ChangeSAMLBinding("new binding"),
idp.ChangeSAMLWithSignedRequest(true),
idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)),
@ -5527,9 +5632,9 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "instance1"),
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "new name",
Metadata: []byte("new metadata"),
Metadata: validSAMLMetadata,
Binding: "new binding",
WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),

View File

@ -10,6 +10,7 @@ import (
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"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/zerrors"
)
@ -446,9 +447,9 @@ func (c *Commands) UpdateOrgLDAPProvider(ctx context.Context, resourceOwner, id
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)
id, err := c.idGenerator.Next()
id, err = c.idGenerator.Next()
if err != nil {
return "", nil, err
}
@ -464,7 +465,7 @@ func (c *Commands) AddOrgSAMLProvider(ctx context.Context, resourceOwner string,
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)
writeModel := NewSAMLOrgIDPWriteModel(resourceOwner, id)
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) {
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
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
}
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) {
events, err := filter(ctx, writeModel.Query())
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) {
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
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 {
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) {
events, err := filter(ctx, writeModel.Query())
if err != nil {

View File

@ -5348,7 +5348,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
type args struct {
ctx context.Context
resourceOwner string
provider SAMLProvider
provider *SAMLProvider
}
type res struct {
id string
@ -5370,7 +5370,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args{
ctx: context.Background(),
resourceOwner: "org1",
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5379,7 +5379,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
},
},
{
"invalid metadata",
"no metadata",
fields{
eventstore: expectEventstore(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
@ -5387,7 +5387,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args{
ctx: context.Background(),
resourceOwner: "org1",
provider: SAMLProvider{
provider: &SAMLProvider{
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",
fields: fields{
@ -5406,7 +5426,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5428,9 +5448,9 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args: args{
ctx: context.Background(),
resourceOwner: "org1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5447,7 +5467,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5475,9 +5495,9 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
args: args{
ctx: context.Background(),
resourceOwner: "org1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
Binding: "binding",
WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
@ -5528,7 +5548,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx context.Context
resourceOwner string
id string
provider SAMLProvider
provider *SAMLProvider
}
type res struct {
want *domain.ObjectDetails
@ -5548,7 +5568,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
args{
ctx: context.Background(),
resourceOwner: "org1",
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5565,7 +5585,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: SAMLProvider{},
provider: &SAMLProvider{},
},
res{
err: func(err error) bool {
@ -5574,7 +5594,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
},
},
{
"invalid metadata",
"no metadata",
fields{
eventstore: expectEventstore(),
},
@ -5582,7 +5602,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
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",
fields: fields{
@ -5603,9 +5643,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5623,7 +5663,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
org.NewSAMLIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
"id1",
"name",
[]byte("metadata"),
validSAMLMetadata,
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
@ -5644,9 +5684,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "name",
Metadata: []byte("metadata"),
Metadata: validSAMLMetadata,
},
},
res: res{
@ -5684,7 +5724,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
"id1",
[]idp.SAMLIDPChanges{
idp.ChangeSAMLName("new name"),
idp.ChangeSAMLMetadata([]byte("new metadata")),
idp.ChangeSAMLMetadata(validSAMLMetadata),
idp.ChangeSAMLBinding("new binding"),
idp.ChangeSAMLWithSignedRequest(true),
idp.ChangeSAMLNameIDFormat(gu.Ptr(domain.SAMLNameIDFormatTransient)),
@ -5707,9 +5747,9 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
id: "id1",
provider: SAMLProvider{
provider: &SAMLProvider{
Name: "new name",
Metadata: []byte("new metadata"),
Metadata: validSAMLMetadata,
Binding: "new binding",
WithSignedRequest: true,
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),

View File

@ -7,7 +7,6 @@ import (
"github.com/pquerna/otp"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/command/preparation"
"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")
return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-SqyJz", "Errors.User.NotFound")
}
if authz.GetCtxData(ctx).UserID != userID {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, human.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUserCredentials(ctx, human.ResourceOwner, userID); err != nil {
return nil, err
}
org, err := c.getOrg(ctx, human.ResourceOwner)
if err != nil {
@ -139,10 +136,8 @@ func (c *Commands) HumanCheckMFATOTPSetup(ctx context.Context, userID, code, use
if err != nil {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, existingOTP.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUserCredentials(ctx, existingOTP.ResourceOwner, userID); err != nil {
return nil, err
}
if existingOTP.State == domain.MFAStateUnspecified || existingOTP.State == domain.MFAStateRemoved {
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 {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-Hd9sd", "Errors.User.MFA.OTP.NotExisting")
}
if userID != authz.GetCtxData(ctx).UserID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUser(ctx, existingOTP.ResourceOwner, userID); err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
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 {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, otpWriteModel.ResourceOwner(), userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUserCredentials(ctx, otpWriteModel.ResourceOwner(), userID); err != nil {
return nil, err
}
if otpWriteModel.otpAdded {
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 {
return nil, err
}
if userID != authz.GetCtxData(ctx).UserID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUser(ctx, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
return nil, err
}
if !existingOTP.otpAdded {
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 {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err := c.checkPermission(ctx, domain.PermissionUserCredentialWrite, otpWriteModel.ResourceOwner(), userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUserCredentials(ctx, otpWriteModel.ResourceOwner(), userID); err != nil {
return nil, err
}
if otpWriteModel.otpAdded {
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 {
return nil, err
}
if userID != authz.GetCtxData(ctx).UserID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUser(ctx, existingOTP.WriteModel.ResourceOwner, userID); err != nil {
return nil, err
}
if !existingOTP.otpAdded {
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
func (c *Commands) setPasswordWithPermission(userID, orgID string) setPasswordVerification {
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/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
@ -146,10 +145,8 @@ func (c *Commands) addHumanWebAuthN(ctx context.Context, userID, resourceowner,
if err != nil {
return nil, nil, nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err = c.checkPermission(ctx, domain.PermissionUserCredentialWrite, user.ResourceOwner, userID); err != nil {
return nil, nil, nil, err
}
if err := c.checkPermissionUpdateUserCredentials(ctx, user.ResourceOwner, userID); err != nil {
return nil, nil, nil, err
}
org, err := c.getOrg(ctx, user.ResourceOwner)
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 {
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 {
return nil, err
}
if err := c.checkPermissionUpdateUser(ctx, existingWebAuthN.ResourceOwner, existingWebAuthN.AggregateID); err != nil {
return nil, err
}
userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel)

View File

@ -127,6 +127,16 @@ func (c *Commands) checkPermissionUpdateUser(ctx context.Context, resourceOwner,
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 {
if userID != "" && userID == authz.GetCtxData(ctx).UserID {
return nil

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
@ -118,10 +117,8 @@ func (c *Commands) changeUserEmailWithGeneratorEvents(ctx context.Context, userI
if err != nil {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil {
return nil, err
}
if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
return nil, err
}
if err = cmd.Change(ctx, domain.EmailAddress(email)); err != nil {
return nil, err
@ -137,10 +134,8 @@ func (c *Commands) resendUserEmailCodeWithGeneratorEvents(ctx context.Context, u
if err != nil {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, cmd.aggregate.ResourceOwner, userID); err != nil {
return nil, err
}
if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
return nil, err
}
if cmd.model.Code == nil {
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/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
@ -74,10 +73,8 @@ func (c *Commands) ResendInviteCode(ctx context.Context, userID, resourceOwner,
if err != nil {
return nil, err
}
if authz.GetCtxData(ctx).UserID != userID {
if err := c.checkPermission(ctx, domain.PermissionUserWrite, existingCode.ResourceOwner, userID); err != nil {
return nil, err
}
if err := c.checkPermissionUpdateUser(ctx, existingCode.ResourceOwner, userID); err != nil {
return nil, err
}
if !existingCode.UserState.Exists() {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-H3b2a", "Errors.User.NotFound")

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
@ -18,7 +17,7 @@ import (
// 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.
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 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
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
eventstore func(t *testing.T) *eventstore.Eventstore
idGenerator id.Generator
checkPermission domain.PermissionCheck
}
type args struct {
userID string
@ -51,18 +52,22 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
wantErr error
}{
{
name: "wrong user",
name: "no permission",
fields: fields{
eventstore: expectEventstore(),
checkPermission: newMockPermissionCheckNotAllowed(),
},
args: args{
userID: "foo",
resourceOwner: "org1",
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",
fields: fields{
eventstore: eventstoreExpect(t,
eventstore: expectEventstore(
expectFilterError(io.ErrClosedPipe),
),
},
@ -76,7 +81,7 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
{
name: "id generator error",
fields: fields{
eventstore: eventstoreExpect(t,
eventstore: expectEventstore(
expectFilter(), // getHumanPasswordlessTokens
expectFilter(eventFromEventPusher(
user.NewHumanAddedEvent(ctx,
@ -118,9 +123,10 @@ func TestCommands_RegisterUserPasskey(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
webauthnConfig: webauthnConfig,
eventstore: tt.fields.eventstore(t),
idGenerator: tt.fields.idGenerator,
webauthnConfig: webauthnConfig,
checkPermission: tt.fields.checkPermission,
}
_, err := c.RegisterUserPasskey(ctx, tt.args.userID, tt.args.resourceOwner, tt.args.rpID, tt.args.authenticator)
require.ErrorIs(t, err, tt.wantErr)

View File

@ -4,7 +4,6 @@ import (
"context"
"io"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/repository/user"
"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 {
return nil, nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Sfe4g", "Errors.User.NotInitialised")
}
if authz.GetCtxData(ctx).UserID != userID {
if err = c.checkPermission(ctx, domain.PermissionUserWrite, model.ResourceOwner, userID); err != nil {
return nil, nil, err
}
if err = c.checkPermissionUpdateUser(ctx, model.ResourceOwner, userID); err != nil {
return nil, nil, err
}
var passwordCode *EncryptedCode
var generatorID string

View File

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

View File

@ -1,15 +1,19 @@
package saml
import (
"bytes"
"context"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/xml"
"io"
"net/url"
"time"
"github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
"golang.org/x/text/encoding/ianaindex"
"github.com/zitadel/zitadel/internal/domain"
"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(
name string,
rootURLStr string,
@ -112,8 +151,8 @@ func New(
key []byte,
options ...ProviderOpts,
) (*Provider, error) {
entityDescriptor := new(saml.EntityDescriptor)
if err := xml.Unmarshal(metadata, entityDescriptor); err != nil {
entityDescriptor, err := ParseMetadata(metadata)
if err != nil {
return nil, err
}
keyPair, err := tls.X509KeyPair(certificate, key)
@ -180,6 +219,7 @@ func (p *Provider) GetSP() (*samlsp.Middleware, error) {
if p.binding != "" {
sp.Binding = p.binding
}
sp.ServiceProvider.MetadataValidDuration = time.Until(sp.ServiceProvider.Certificate.NotAfter)
return sp, nil
}

View File

@ -1,6 +1,7 @@
package saml
import (
"encoding/xml"
"testing"
"github.com/crewjam/saml"
@ -10,6 +11,7 @@ import (
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/idp/providers/saml/requesttracker"
"github.com/zitadel/zitadel/internal/zerrors"
)
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/samlsp"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/idp"
"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 {
invalidRespErr := new(saml.InvalidResponseError)
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")
}

View File

@ -134,7 +134,7 @@ func TestSession_FetchUser(t *testing.T) {
requestID: "id-b22c90db88bf01d82ffb0a7b6fe25ac9fcb2c679",
},
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