From 001fb9761bda8d3463a84f6f6a800651a3f21549 Mon Sep 17 00:00:00 2001 From: Ivan <88590094+Nexfader@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:34:54 +0300 Subject: [PATCH 1/7] fix(i18n): Improve Russian locale in the auth module (#8988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Which Problems Are Solved - The quality of the Russian locale in the auth module is currently low, likely due to automatic translation. # How the Problems Are Solved - Corrected grammatical errors and awkward phrasing from auto-translation (e.g., "footer" → ~"нижний колонтитул"~ "примечание"). - Enhanced alignment with the English (reference) locale, including improvements to casing and semantics. - Ensured consistency in terminology (e.g., the "next"/"cancel" buttons are now consistently translated as "продолжить"/"отмена"). - Improved clarity and readability (e.g., "подтверждение пароля" → "повторите пароль"). # Additional Changes N/A # Additional Context - Follow-up for PR #6864 Co-authored-by: Fabi --- internal/api/ui/login/static/i18n/ru.yaml | 423 +++++++++++----------- 1 file changed, 212 insertions(+), 211 deletions(-) diff --git a/internal/api/ui/login/static/i18n/ru.yaml b/internal/api/ui/login/static/i18n/ru.yaml index 9c1796c59a..e6edc967fb 100644 --- a/internal/api/ui/login/static/i18n/ru.yaml +++ b/internal/api/ui/login/static/i18n/ru.yaml @@ -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: Имя @@ -268,19 +269,19 @@ RegistrationUser: Male: Мужской Diverse: Другой / X PasswordLabel: Пароль - PasswordConfirmLabel: Подтверждение пароля + PasswordConfirmLabel: Повторите пароль TosAndPrivacyLabel: Условия использования TosConfirm: Я согласен с TosLinkText: Пользовательским соглашением PrivacyConfirm: Я согласен с PrivacyLinkText: Политикой конфиденциальности - ExternalLogin: или зарегистрируйтесь внешним пользователем - BackButtonText: вход - NextButtonText: далее + ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя + BackButtonText: Войти + NextButtonText: Продолжить ExternalRegistrationUserOverview: Title: Регистрация внешнего пользователя - Description: Мы получили ваши данные пользователя у выбранного провайдера. Теперь вы можете изменить или дополнить их. + Description: Мы получили ваши данные от провайдера. Вы можете изменить или дополнить их. EmailLabel: Электронная почта UsernameLabel: Логин FirstnameLabel: Имя @@ -310,9 +311,9 @@ ExternalRegistrationUserOverview: TosLinkText: Пользовательским соглашением PrivacyConfirm: Я согласен с PrivacyLinkText: Политикой конфиденциальности - ExternalLogin: или зарегистрируйтесь внешним пользователем - BackButtonText: назад - NextButtonText: сохранить + ExternalLogin: или зарегистрируйтесь с помощью внешнего пользователя + BackButtonText: Назад + NextButtonText: Сохранить RegistrationOrg: Title: Регистрация организации @@ -323,7 +324,7 @@ RegistrationOrg: FirstnameLabel: Имя LastnameLabel: Фамилия PasswordLabel: Пароль - PasswordConfirmLabel: Подтверждение пароля + PasswordConfirmLabel: Повторите пароль TosAndPrivacyLabel: Условия использования TosConfirm: Я согласен с TosLinkText: Пользовательским соглашением @@ -333,32 +334,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: Пользовательским соглашением @@ -386,132 +387,132 @@ 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: Код пользователя не существует From 8fcf8e9ac8333aa439eef2af2fce9deb4201f080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= Date: Mon, 2 Dec 2024 09:57:33 +0100 Subject: [PATCH 2/7] docs: Add adopters (#8987) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I love Zitadel, and we have been using it for a while. It's the most complete solution out there. ❤️ Co-authored-by: Fabi --- ADOPTERS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 2f903664be..ba740212b2 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -12,5 +12,7 @@ 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 | +| 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 | From c0a93944c30f1ed4f10b265bd0255d7ebe7f999a Mon Sep 17 00:00:00 2001 From: Kim JeongHyeon <38876544+KimTibber@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:11:31 +0900 Subject: [PATCH 3/7] feat(i18n): add korean language support (#8879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello everyone, To support Korean-speaking users who may experience challenges in using this excellent tool due to language barriers, I have added Korean language support with the help of ChatGPT. I hope that this contribution allows ZITADEL to be more useful and accessible to Korean-speaking users. Thank you. --- 안녕하세요 여러분, 언어의 어려움으로 이 훌륭한 도구를 활용하는데 곤란함을 겪는 한국어 사용자들을 위하여 ChatGPT의 도움을 받아 한국어 지원을 추가하였습니다. 이 기여를 통해 ZITADEL이 한국어 사용자들에게 유용하게 활용되었으면 좋겠습니다. 감사합니다. Co-authored-by: Max Peintner --- console/src/app/app.module.ts | 3 + console/src/app/utils/language.ts | 3 +- console/src/assets/i18n/bg.json | 9 +- console/src/assets/i18n/cs.json | 9 +- console/src/assets/i18n/de.json | 9 +- console/src/assets/i18n/en.json | 9 +- console/src/assets/i18n/es.json | 9 +- console/src/assets/i18n/fr.json | 9 +- console/src/assets/i18n/hu.json | 6 +- console/src/assets/i18n/id.json | 9 +- console/src/assets/i18n/it.json | 9 +- console/src/assets/i18n/ja.json | 9 +- console/src/assets/i18n/ko.json | 2671 +++++++++++++++++ console/src/assets/i18n/mk.json | 9 +- console/src/assets/i18n/nl.json | 6 +- console/src/assets/i18n/pl.json | 9 +- console/src/assets/i18n/pt.json | 9 +- console/src/assets/i18n/ru.json | 9 +- console/src/assets/i18n/sv.json | 9 +- console/src/assets/i18n/zh.json | 9 +- docs/docs/guides/manage/customize/texts.md | 1 + internal/api/ui/login/static/i18n/bg.yaml | 3 + internal/api/ui/login/static/i18n/cs.yaml | 3 + internal/api/ui/login/static/i18n/de.yaml | 3 + internal/api/ui/login/static/i18n/en.yaml | 3 + internal/api/ui/login/static/i18n/es.yaml | 3 + internal/api/ui/login/static/i18n/fr.yaml | 3 + internal/api/ui/login/static/i18n/hu.yaml | 3 + internal/api/ui/login/static/i18n/id.yaml | 3 + internal/api/ui/login/static/i18n/it.yaml | 3 + internal/api/ui/login/static/i18n/ja.yaml | 3 + internal/api/ui/login/static/i18n/ko.yaml | 521 ++++ internal/api/ui/login/static/i18n/mk.yaml | 3 + internal/api/ui/login/static/i18n/nl.yaml | 3 + internal/api/ui/login/static/i18n/pl.yaml | 3 + internal/api/ui/login/static/i18n/pt.yaml | 3 + internal/api/ui/login/static/i18n/ru.yaml | 3 + internal/api/ui/login/static/i18n/sv.yaml | 3 + internal/api/ui/login/static/i18n/zh.yaml | 3 + .../templates/external_not_found_option.html | 2 + internal/notification/static/i18n/ko.yaml | 68 + internal/static/i18n/ko.yaml | 1406 +++++++++ 42 files changed, 4823 insertions(+), 50 deletions(-) create mode 100644 console/src/assets/i18n/ko.json create mode 100644 internal/api/ui/login/static/i18n/ko.yaml create mode 100644 internal/notification/static/i18n/ko.yaml create mode 100644 internal/static/i18n/ko.yaml diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts index b995c69b88..7fad84a4c6 100644 --- a/console/src/app/app.module.ts +++ b/console/src/app/app.module.ts @@ -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 { diff --git a/console/src/app/utils/language.ts b/console/src/app/utils/language.ts index f17893372c..4ef63dcb28 100644 --- a/console/src/app/utils/language.ts +++ b/console/src/app/utils/language.ts @@ -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'; diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json index d47d411e07..c196e230a1 100644 --- a/console/src/assets/i18n/bg.json +++ b/console/src/assets/i18n/bg.json @@ -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": "Проверката на имейл е извършена", @@ -2559,7 +2561,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Добавяне на мениджър", diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json index f251da7ab5..817587574f 100644 --- a/console/src/assets/i18n/cs.json +++ b/console/src/assets/i18n/cs.json @@ -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", @@ -2572,7 +2574,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Přidat manažera", diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 3659848bc7..b8f0e3285b 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -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", @@ -2563,7 +2565,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Manager hinzufügen", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index c6fe05499d..d18a7114fe 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -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", @@ -2588,7 +2590,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Add a Manager", diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index 9565c034b7..2367e12471 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -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", @@ -2560,7 +2562,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Añadir un Mánager", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 5a41984c7e..72423c79ec 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -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", @@ -2564,7 +2566,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Ajouter un responsable", diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json index 614af5520e..064dd8ea5f 100644 --- a/console/src/assets/i18n/hu.json +++ b/console/src/assets/i18n/hu.json @@ -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", diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json index 76fe69b507..9dd80d7902 100644 --- a/console/src/assets/i18n/id.json +++ b/console/src/assets/i18n/id.json @@ -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", @@ -2272,7 +2274,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Tambahkan Manajer", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index a87cb2fc75..f69e97bea7 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -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.", @@ -2564,7 +2566,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Aggiungi un manager", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 4e7e401c7a..7e9f102ecf 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -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": "メール認証が完了しました", @@ -2554,7 +2556,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "マネージャーを追加する", diff --git a/console/src/assets/i18n/ko.json b/console/src/assets/i18n/ko.json new file mode 100644 index 0000000000..79fe95324a --- /dev/null +++ b/console/src/assets/i18n/ko.json @@ -0,0 +1,2671 @@ +{ + "APP_NAME": "ZITADEL", + "DESCRIPTIONS": { + "METADATA_TITLE": "메타데이터", + "HOME": { + "TITLE": "ZITADEL 시작하기", + "NEXT": { + "TITLE": "다음 단계", + "DESCRIPTION": "애플리케이션 보안을 위해 다음 단계를 완료하세요.", + "CREATE_PROJECT": { + "TITLE": "프로젝트 생성", + "DESCRIPTION": "프로젝트를 추가하고 역할과 권한을 정의하세요." + } + }, + "MORE_SHORTCUTS": { + "GET_STARTED": { + "TITLE": "시작하기", + "DESCRIPTION": "빠른 시작 가이드를 단계별로 따라하여 바로 시작하세요." + }, + "DOCS": { + "TITLE": "문서", + "DESCRIPTION": "ZITADEL의 지식 베이스를 탐색하여 핵심 개념과 아이디어에 대해 배우세요. ZITADEL의 작동 방식과 사용 방법을 학습하세요." + }, + "EXAMPLES": { + "TITLE": "예제 및 소프트웨어 개발 키트", + "DESCRIPTION": "예제와 SDK를 통해 ZITADEL을 선호하는 프로그래밍 언어 및 도구와 함께 사용하는 방법을 확인하세요." + } + } + }, + "ORG": { + "TITLE": "조직", + "DESCRIPTION": "조직은 사용자, 앱이 포함된 프로젝트, ID 제공자, 회사 브랜딩과 같은 설정을 관리합니다. 여러 조직 간에 설정을 공유하고 싶으신가요? 기본 설정을 구성하세요.", + "METADATA": "조직에 위치나 다른 시스템의 식별자와 같은 맞춤 속성을 추가하세요. 이 정보를 작업에 활용할 수 있습니다." + }, + "PROJECTS": { + "TITLE": "프로젝트", + "DESCRIPTION": "프로젝트는 하나 이상의 애플리케이션을 호스팅하여 사용자를 인증할 수 있습니다. 또한 프로젝트를 통해 사용자에게 권한을 부여할 수 있습니다. 다른 조직의 사용자가 애플리케이션에 로그인할 수 있도록 프로젝트에 대한 액세스를 부여하세요.

프로젝트를 찾을 수 없는 경우 프로젝트 소유자 또는 관련 권한이 있는 사람에게 문의하여 액세스를 얻으세요.", + "OWNED": { + "TITLE": "소유한 프로젝트", + "DESCRIPTION": "이 프로젝트는 사용자가 소유한 프로젝트입니다. 프로젝트 설정, 권한 및 애플리케이션을 관리할 수 있습니다." + }, + "GRANTED": { + "TITLE": "부여된 프로젝트", + "DESCRIPTION": "다른 조직에서 사용자가 부여받은 프로젝트입니다. 부여된 프로젝트를 통해 사용자가 다른 조직의 애플리케이션에 액세스할 수 있습니다." + } + }, + "USERS": { + "TITLE": "사용자", + "DESCRIPTION": "사용자는 애플리케이션에 액세스할 수 있는 사람 또는 장치입니다.", + "HUMANS": { + "TITLE": "사용자", + "DESCRIPTION": "사용자는 로그인 프롬프트로 브라우저 세션에서 상호 인증을 수행합니다.", + "METADATA": "부서와 같은 사용자 정의 속성을 사용자에게 추가하세요. 이 정보를 작업에 사용할 수 있습니다." + }, + "MACHINES": { + "TITLE": "서비스 사용자", + "DESCRIPTION": "서비스 사용자는 개인 키로 서명된 JWT bearer 토큰을 사용하여 비대화형 인증을 수행합니다. 또한 개인 액세스 토큰을 사용할 수 있습니다.", + "METADATA": "인증 시스템과 같은 사용자 정의 속성을 사용자에게 추가하세요. 이 정보를 작업에 사용할 수 있습니다." + }, + "SELF": { + "METADATA": "부서와 같은 사용자 정의 속성을 사용자에게 추가하세요. 이 정보를 조직의 작업에 사용할 수 있습니다." + } + }, + "AUTHORIZATIONS": { + "TITLE": "권한", + "DESCRIPTION": "권한은 사용자의 프로젝트 접근 권한을 정의합니다. 사용자가 프로젝트에 액세스할 수 있도록 하고, 프로젝트 내의 역할을 정의할 수 있습니다." + }, + "ACTIONS": { + "TITLE": "작업", + "DESCRIPTION": "사용자가 ZITADEL에 인증할 때 발생하는 이벤트에 맞춰 사용자 정의 코드를 실행하세요. 프로세스를 자동화하고, 사용자 메타데이터 및 토큰을 강화하거나 외부 시스템에 알림을 보냅니다.", + "SCRIPTS": { + "TITLE": "스크립트", + "DESCRIPTION": "하나의 자바스크립트 코드로 여러 작업 플로우에서 손쉽게 활용하세요." + }, + "FLOWS": { + "TITLE": "플로우", + "DESCRIPTION": "인증 플로우를 선택하고 이 플로우 내의 특정 이벤트에서 작업을 트리거하세요." + } + }, + "SETTINGS": { + "INSTANCE": { + "TITLE": "기본 설정", + "DESCRIPTION": "모든 조직에 대한 기본 설정입니다. 권한이 있으면 일부 설정을 조직 설정에서 재정의할 수 있습니다." + }, + "ORG": { + "TITLE": "조직 설정", + "DESCRIPTION": "조직의 설정을 사용자 정의하세요." + }, + "FEATURES": { + "TITLE": "기능 설정", + "DESCRIPTION": "인스턴스의 기능을 잠금 해제하세요." + }, + "IDPS": { + "TITLE": "ID 제공자", + "DESCRIPTION": "외부 ID 제공자를 생성하고 활성화하세요. 알려진 제공자를 선택하거나 다른 OIDC, OAuth 또는 SAML 호환 제공자를 구성하세요. 기존 JWT 토큰을 사용하여 연합된 ID로 구성할 수도 있습니다.", + "NEXT": "다음 단계는?", + "SAML": { + "TITLE": "SAML ID 제공자 구성", + "DESCRIPTION": "ZITADEL이 구성되었습니다. 이제 SAML ID 제공자에 대한 일부 설정이 필요합니다. 대부분의 제공자는 ZITADEL 메타데이터 XML 전체를 업로드하는 기능을 제공합니다. 일부 제공자는 메타데이터 URL, Assertion Consumer Service (ACS) URL, 또는 Single Logout URL과 같은 특정 URL만 제공합니다." + }, + "CALLBACK": { + "TITLE": "{{ provider }} ID 제공자 구성", + "DESCRIPTION": "ZITADEL을 구성하기 전에, 인증 후 ZITADEL로 브라우저 리디렉션을 가능하게 하기 위해 이 URL을 ID 제공자에게 전달하세요." + }, + "JWT": { + "TITLE": "JWT를 연합된 ID로 사용", + "DESCRIPTION": "JWT ID 제공자를 통해 기존 JWT 토큰을 연합된 ID로 사용할 수 있습니다. 이미 JWT 발급자가 있는 경우 이 기능이 유용합니다. JWT IdP를 사용하여 ZITADEL에서 사용자 생성 및 업데이트를 실시간으로 수행할 수 있습니다." + }, + "LDAP": { + "TITLE": "LDAP ID 제공자에 ZITADEL 연결 구성", + "DESCRIPTION": "LDAP 서버의 연결 세부 정보를 제공하고, LDAP 속성을 ZITADEL 속성에 매핑하세요." + }, + "AUTOFILL": { + "TITLE": "사용자 데이터 자동 입력", + "DESCRIPTION": "사용자 경험을 개선하기 위해 작업을 사용하세요. ID 제공자에서 값을 가져와 ZITADEL의 등록 양식을 미리 채울 수 있습니다." + }, + "ACTIVATE": { + "TITLE": "ID 제공자 활성화", + "DESCRIPTION": "ID 제공자가 아직 활성화되지 않았습니다. 사용자가 로그인할 수 있도록 활성화하세요." + } + }, + "PW_COMPLEXITY": { + "TITLE": "비밀번호 복잡성", + "DESCRIPTION": "사용자가 복잡한 비밀번호를 사용하도록 규칙을 정의하여 보안을 강화하세요." + }, + "BRANDING": { + "TITLE": "브랜딩", + "DESCRIPTION": "로그인 폼의 외관을 사용자 정의하세요. 설정을 완료한 후 구성 적용을 기억하세요." + }, + "PRIVACY_POLICY": { + "TITLE": "외부 링크", + "DESCRIPTION": "사용자가 로그인 페이지에서 커스텀 외부 리소스로 안내됩니다. 사용자는 가입 전에 이용 약관과 개인정보처리방침을 수락해야 합니다. 문서 링크를 변경하거나, 콘솔에서 문서 버튼을 숨기려면 빈 문자열로 설정하세요. 사용자 정의 외부 링크와 그 링크에 대한 설명을 콘솔에 추가하거나, 버튼을 숨기려면 빈 값으로 설정하세요." + }, + "SMTP_PROVIDER": { + "TITLE": "SMTP 설정", + "DESCRIPTION": "사용자가 신뢰할 수 있는 도메인을 발신자 주소로 사용하는 SMTP 서버를 구성하세요." + }, + "SMS_PROVIDER": { + "TITLE": "SMS 설정", + "DESCRIPTION": "ZITADEL의 모든 기능을 사용하려면 Twilio를 설정하여 사용자에게 SMS 메시지를 보내세요." + }, + "IAM_EVENTS": { + "TITLE": "이벤트", + "DESCRIPTION": "인스턴스에서 상태 변경을 보여주는 페이지입니다. 디버깅을 위해 시간 범위별로 필터링하거나 감사 목적으로 특정 집계를 필터링하세요." + }, + "IAM_FAILED_EVENTS": { + "TITLE": "실패한 이벤트", + "DESCRIPTION": "인스턴스에서 실패한 모든 이벤트가 표시됩니다. ZITADEL이 예상대로 작동하지 않는 경우, 이 목록을 먼저 확인하세요." + }, + "IAM_VIEWS": { + "TITLE": "뷰", + "DESCRIPTION": "데이터베이스 뷰와 최근 이벤트가 처리된 시점을 보여주는 페이지입니다. 데이터가 누락된 경우, 뷰가 최신인지 확인하세요." + }, + "LANGUAGES": { + "TITLE": "언어", + "DESCRIPTION": "로그인 폼과 알림 메시지가 번역되는 언어를 제한하세요. 일부 언어를 비활성화하려면 '허용되지 않은 언어' 섹션으로 드래그하세요. 기본 언어로 허용된 언어를 지정할 수 있습니다. 사용자의 선호 언어가 허용되지 않으면 기본 언어가 사용됩니다." + }, + "SECRET_GENERATORS": { + "TITLE": "시크릿 생성기", + "DESCRIPTION": "시크릿의 복잡성과 수명을 정의하세요. 높은 복잡성과 긴 수명은 보안을 강화하고, 낮은 복잡성과 짧은 수명은 암호 해독 성능을 향상시킵니다." + }, + "SECURITY": { + "TITLE": "보안 설정", + "DESCRIPTION": "보안에 영향을 미칠 수 있는 ZITADEL 기능을 활성화하세요. 설정을 변경하기 전에 주의 깊게 검토하세요." + }, + "OIDC": { + "TITLE": "OpenID Connect 설정", + "DESCRIPTION": "OIDC 토큰 수명을 설정하세요. 짧은 수명은 사용자의 보안을 강화하고, 긴 수명은 사용자의 편의를 증가시킵니다.", + "LABEL_HOURS": "최대 수명 (시간)", + "LABEL_DAYS": "최대 수명 (일)", + "ACCESS_TOKEN": { + "TITLE": "액세스 토큰", + "DESCRIPTION": "액세스 토큰은 사용자를 인증하는 데 사용되는 단기 토큰입니다. 사용자의 데이터를 접근하는 데 사용됩니다. 불법적인 접근 위험을 줄이기 위해 짧은 수명을 사용하세요. 액세스 토큰은 갱신 토큰을 사용하여 자동으로 갱신할 수 있습니다." + }, + "ID_TOKEN": { + "TITLE": "ID 토큰", + "DESCRIPTION": "ID 토큰은 사용자에 대한 정보를 포함하는 JSON 웹 토큰(JWT)입니다. ID 토큰 수명은 액세스 토큰 수명을 초과해서는 안 됩니다." + }, + "REFRESH_TOKEN": { + "TITLE": "갱신 토큰", + "DESCRIPTION": "갱신 토큰은 새로운 액세스 토큰을 얻는 데 사용되는 장기 토큰입니다. 갱신 토큰이 만료되면 사용자가 수동으로 재인증해야 합니다." + }, + "REFRESH_TOKEN_IDLE": { + "TITLE": "유휴 갱신 토큰", + "DESCRIPTION": "유휴 갱신 토큰 수명은 갱신 토큰이 사용되지 않는 최대 기간을 의미합니다." + } + }, + "MESSAGE_TEXTS": { + "TITLE": "메시지 텍스트", + "DESCRIPTION": "알림 이메일 또는 SMS 메시지의 텍스트를 사용자 정의하세요. 언어를 비활성화하려면 인스턴스의 언어 설정에서 제한하세요.", + "TYPE_DESCRIPTIONS": { + "DC": "조직의 도메인을 주장할 때, 해당 도메인을 로그인 이름에 사용하지 않는 사용자는 로그인 이름을 도메인에 맞게 변경하도록 요청받습니다.", + "INIT": "사용자가 생성되면 비밀번호를 설정할 수 있는 링크가 포함된 이메일을 받게 됩니다.", + "PC": "사용자가 비밀번호를 변경하면 알림 설정을 통해 변경 사항에 대한 알림을 받게 됩니다.", + "PL": "사용자가 비밀번호 없는 인증 방식을 추가하면 이메일의 링크를 클릭하여 활성화해야 합니다.", + "PR": "사용자가 비밀번호를 재설정할 때 새 비밀번호를 설정할 수 있는 링크가 포함된 이메일을 받습니다.", + "VE": "사용자가 이메일 주소를 변경할 때, 새 주소를 확인할 수 있는 링크가 포함된 이메일을 받습니다.", + "VP": "사용자가 전화번호를 변경할 때, 새 번호를 확인할 수 있는 코드가 포함된 SMS를 받습니다.", + "VEO": "사용자가 이메일을 통해 일회성 비밀번호를 추가할 때, 이메일 주소로 전송된 코드를 입력하여 활성화해야 합니다.", + "VSO": "사용자가 SMS를 통해 일회성 비밀번호를 추가할 때, 전화번호로 전송된 코드를 입력하여 활성화해야 합니다.", + "IU": "사용자 초대 코드가 생성되면 인증 방법을 설정할 수 있는 링크가 포함된 이메일을 받습니다." + } + }, + "LOGIN_TEXTS": { + "TITLE": "로그인 인터페이스 텍스트", + "DESCRIPTION": "로그인 양식의 텍스트를 사용자 정의하세요. 텍스트가 비어 있으면 기본 값이 표시됩니다. 언어를 비활성화하려면 인스턴스의 언어 설정에서 제한하세요." + }, + "DOMAINS": { + "TITLE": "도메인 설정", + "DESCRIPTION": "도메인에 대한 제한 사항을 정의하고 로그인 이름 패턴을 구성하세요.", + "REQUIRE_VERIFICATION": { + "TITLE": "맞춤 도메인 확인 필요", + "DESCRIPTION": "활성화된 경우, 조직 도메인은 도메인 검색이나 사용자 이름 접미사로 사용되기 전에 확인되어야 합니다." + }, + "LOGIN_NAME_PATTERN": { + "TITLE": "로그인 이름 패턴", + "DESCRIPTION": "사용자의 로그인 이름 패턴을 제어하세요. 사용자가 로그인 이름을 입력하는 즉시 ZITADEL은 사용자의 조직을 선택합니다. 따라서 로그인 이름은 모든 조직에서 고유해야 합니다. 여러 도메인에 계정이 있는 사용자의 경우, 로그인 이름에 조직 도메인을 접미사로 붙여 고유성을 보장할 수 있습니다." + }, + "DOMAIN_VERIFICATION": { + "TITLE": "도메인 확인", + "DESCRIPTION": "조직이 실제로 제어하는 도메인만 사용할 수 있도록 합니다. 활성화되면 조직 도메인은 도메인 하이재킹 방지를 위해 DNS 또는 HTTP 검증을 통해 주기적으로 확인됩니다." + }, + "SMTP_SENDER_ADDRESS": { + "TITLE": "SMTP 발신 주소", + "DESCRIPTION": "인스턴스 도메인 중 하나와 일치하는 경우에만 SMTP 발신 주소를 허용합니다." + } + }, + "LOGIN": { + "LIFETIMES": { + "TITLE": "로그인 수명", + "DESCRIPTION": "로그인 관련 최대 수명을 줄여 보안을 강화하세요.", + "LABEL": "최대 수명 (시간)", + "PW_CHECK": { + "TITLE": "비밀번호 확인", + "DESCRIPTION": "지정된 기간 후 사용자는 비밀번호로 재인증이 필요합니다." + }, + "EXT_LOGIN_CHECK": { + "TITLE": "외부 로그인 확인", + "DESCRIPTION": "지정된 기간 후 사용자는 외부 ID 제공자에게 리디렉션됩니다." + }, + "MULTI_FACTOR_INIT": { + "TITLE": "다중 인증 초기화 확인", + "DESCRIPTION": "사용자가 아직 설정하지 않은 경우, 지정된 기간 후 두 번째 인증 요소나 다중 인증을 설정하도록 안내합니다. 수명을 0으로 설정하면 이 안내가 비활성화됩니다." + }, + "SECOND_FACTOR_CHECK": { + "TITLE": "두 번째 인증 요소 확인", + "DESCRIPTION": "사용자는 지정된 기간 동안 두 번째 인증 요소를 재확인해야 합니다." + }, + "MULTI_FACTOR_CHECK": { + "TITLE": "다중 인증 확인", + "DESCRIPTION": "사용자는 지정된 기간 동안 다중 인증을 재확인해야 합니다." + } + }, + "FORM": { + "TITLE": "로그인 폼", + "DESCRIPTION": "로그인 폼을 사용자 정의하세요.", + "USERNAME_PASSWORD_ALLOWED": { + "TITLE": "사용자 이름과 비밀번호 허용", + "DESCRIPTION": "사용자가 사용자 이름과 비밀번호로 로그인할 수 있도록 허용하세요. 비활성화하면 비밀번호 없는 인증 또는 외부 ID 제공자로만 로그인할 수 있습니다." + }, + "USER_REGISTRATION_ALLOWED": { + "TITLE": "사용자 등록 허용", + "DESCRIPTION": "익명 사용자가 계정을 생성할 수 있도록 허용하세요." + }, + "ORG_REGISTRATION_ALLOWED": { + "TITLE": "조직 등록 허용", + "DESCRIPTION": "익명 사용자가 조직을 생성할 수 있도록 허용하세요." + }, + "EXTERNAL_LOGIN_ALLOWED": { + "TITLE": "외부 로그인 허용", + "DESCRIPTION": "사용자가 ZITADEL 사용자 대신 외부 ID 제공자를 통해 로그인할 수 있도록 허용하세요." + }, + "HIDE_PASSWORD_RESET": { + "TITLE": "비밀번호 재설정 숨기기", + "DESCRIPTION": "사용자가 비밀번호를 재설정할 수 없도록 허용하지 않습니다." + }, + "DOMAIN_DISCOVERY_ALLOWED": { + "TITLE": "도메인 검색 허용", + "DESCRIPTION": "예를 들어 이메일 주소와 같은 로그인 이름의 도메인에 따라 사용자의 조직을 찾습니다." + }, + "IGNORE_UNKNOWN_USERNAMES": { + "TITLE": "알 수 없는 사용자 이름 무시", + "DESCRIPTION": "활성화된 경우, 로그인 폼은 사용자 이름이 알 수 없는 경우에도 오류 메시지를 표시하지 않습니다. 이는 사용자 이름 추측을 방지하는 데 도움이 됩니다." + }, + "DISABLE_EMAIL_LOGIN": { + "TITLE": "이메일 로그인 비활성화", + "DESCRIPTION": "활성화된 경우, 사용자는 이메일 주소를 사용하여 로그인할 수 없습니다. 이 설정을 비활성화하면 모든 조직에서 사용자의 이메일 주소가 고유해야 합니다." + }, + "DISABLE_PHONE_LOGIN": { + "TITLE": "전화번호 로그인 비활성화", + "DESCRIPTION": "활성화된 경우, 사용자는 전화번호를 사용하여 로그인할 수 없습니다. 이 설정을 비활성화하면 모든 조직에서 사용자의 전화번호가 고유해야 합니다." + } + } + } + } + }, + "PAGINATOR": { + "PREVIOUS": "이전", + "NEXT": "다음", + "COUNT": "개의 결과를 찾았습니다", + "MORE": "더보기" + }, + "FOOTER": { + "LINKS": { + "CONTACT": "문의하기", + "TOS": "이용 약관", + "PP": "개인정보처리방침" + }, + "THEME": { + "DARK": "다크", + "LIGHT": "라이트" + } + }, + "HOME": { + "WELCOME": "ZITADEL 시작하기", + "DISCLAIMER": "ZITADEL은 귀하의 데이터를 기밀하고 안전하게 처리합니다.", + "DISCLAIMERLINK": "추가 정보", + "DOCUMENTATION": { + "DESCRIPTION": "ZITADEL 시작을 빠르게 진행하세요." + }, + "GETSTARTED": { + "DESCRIPTION": "ZITADEL 시작을 빠르게 진행하세요." + }, + "QUICKSTARTS": { + "LABEL": "첫 단계", + "DESCRIPTION": "ZITADEL 시작을 빠르게 진행하세요." + }, + "SHORTCUTS": { + "SHORTCUTS": "바로 가기", + "SETTINGS": "사용 가능한 바로 가기", + "PROJECTS": "프로젝트", + "REORDER": "타일을 클릭 후 드래그하여 이동하세요", + "ADD": "타일을 클릭 후 드래그하여 추가하세요" + } + }, + "ONBOARDING": { + "DESCRIPTION": "다음 단계", + "MOREDESCRIPTION": "더 많은 바로 가기", + "COMPLETED": "완료됨", + "DISMISS": "괜찮습니다. 전문가입니다.", + "CARD": { + "TITLE": "ZITADEL 설정", + "DESCRIPTION": "이 체크리스트는 인스턴스를 설정하는 데 도움이 되며, 가장 중요한 단계를 안내합니다." + }, + "MILESTONES": { + "instance.policy.label.added": { + "title": "브랜드 설정", + "description": "로그인의 색상과 모양을 정의하고 로고와 아이콘을 업로드하세요.", + "action": "브랜딩 설정" + }, + "instance.smtp.config.added": { + "title": "SMTP 설정", + "description": "자신의 메일 서버 설정을 구성하세요.", + "action": "SMTP 설정" + }, + "PROJECT_CREATED": { + "title": "프로젝트 생성", + "description": "프로젝트를 추가하고 역할과 권한을 정의하세요.", + "action": "프로젝트 생성" + }, + "APPLICATION_CREATED": { + "title": "앱 등록", + "description": "웹, 네이티브, API 또는 SAML 애플리케이션을 등록하고 인증 플로우를 설정하세요.", + "action": "앱 등록" + }, + "AUTHENTICATION_SUCCEEDED_ON_APPLICATION": { + "title": "앱에 로그인", + "description": "ZITADEL로 애플리케이션을 통합하고 관리자 사용자로 로그인하여 테스트하세요.", + "action": "로그인" + }, + "user.human.added": { + "title": "사용자 추가", + "description": "애플리케이션 사용자 추가", + "action": "사용자 추가" + }, + "user.grant.added": { + "title": "사용자 권한 부여", + "description": "사용자가 애플리케이션에 접근할 수 있도록 하고 역할을 설정하세요.", + "action": "사용자 권한 부여" + } + } + }, + "MENU": { + "INSTANCE": "기본 설정", + "DASHBOARD": "홈", + "PERSONAL_INFO": "개인 정보", + "DOCUMENTATION": "문서", + "INSTANCEOVERVIEW": "인스턴스", + "ORGS": "조직", + "VIEWS": "뷰", + "EVENTS": "이벤트", + "FAILEDEVENTS": "실패한 이벤트", + "ORGANIZATION": "조직", + "PROJECT": "프로젝트", + "PROJECTOVERVIEW": "개요", + "PROJECTGRANTS": "권한 부여", + "ROLES": "역할", + "GRANTEDPROJECT": "부여된 프로젝트", + "HUMANUSERS": "사용자", + "MACHINEUSERS": "서비스 사용자", + "LOGOUT": "모든 사용자 로그아웃", + "NEWORG": "새 조직", + "IAMADMIN": "IAM 관리자입니다. 확장된 권한이 부여되었습니다.", + "SHOWORGS": "모든 조직 보기", + "GRANTS": "권한", + "ACTIONS": "작업", + "PRIVACY": "개인정보처리방침", + "TOS": "이용 약관", + "OPENSHORTCUTSTOOLTIP": "키보드 단축키 표시하려면 ? 입력", + "SETTINGS": "설정", + "CUSTOMERPORTAL": "고객 포털" + }, + "QUICKSTART": { + "TITLE": "애플리케이션에 ZITADEL 통합", + "DESCRIPTION": "애플리케이션에 ZITADEL을 통합하거나 샘플 중 하나를 사용하여 몇 분 안에 시작하세요.", + "BTN_START": "애플리케이션 생성", + "BTN_LEARNMORE": "자세히 알아보기", + "CREATEPROJECTFORAPP": "프로젝트 생성 {{value}}", + "SELECT_FRAMEWORK": "프레임워크 선택", + "FRAMEWORK": "프레임워크", + "FRAMEWORK_OTHER": "기타 (OIDC, SAML, API)", + "ALMOSTDONE": "거의 완료되었습니다.", + "REVIEWCONFIGURATION": "구성 검토", + "REVIEWCONFIGURATION_DESCRIPTION": "{{value}} 애플리케이션에 대한 기본 구성을 생성했습니다. 생성 후 이 구성을 필요에 맞게 조정할 수 있습니다.", + "REDIRECTS": "리디렉션 구성", + "DEVMODEWARN": "개발 모드가 기본으로 활성화되어 있습니다. 운영 환경에서는 나중에 설정 값을 변경할 수 있습니다.", + "GUIDE": "가이드", + "BROWSEEXAMPLES": "예제 및 SDK 둘러보기", + "DUPLICATEAPPRENAME": "같은 이름의 앱이 이미 존재합니다. 다른 이름을 선택하세요.", + "DIALOG": { + "CHANGE": { + "TITLE": "프레임워크 변경", + "DESCRIPTION": "애플리케이션의 빠른 설정을 위해 사용할 수 있는 프레임워크 중 하나를 선택하세요." + } + } + }, + "ACTIONS": { + "ACTIONS": "작업", + "FILTER": "필터", + "RENAME": "이름 변경", + "SET": "설정", + "COPY": "클립보드에 복사", + "COPIED": "클립보드에 복사되었습니다.", + "RESET": "재설정", + "RESETDEFAULT": "기본값으로 재설정", + "RESETTO": "다음으로 재설정: ", + "RESETCURRENT": "현재 값으로 재설정", + "SHOW": "표시", + "HIDE": "숨기기", + "SAVE": "저장", + "SAVENOW": "지금 저장", + "NEW": "새로 만들기", + "ADD": "추가", + "CREATE": "생성", + "CONTINUE": "계속", + "CONTINUEWITH": "{{value}}으로 계속", + "BACK": "뒤로", + "CLOSE": "닫기", + "CLEAR": "비우기", + "CANCEL": "취소", + "INFO": "정보", + "OK": "확인", + "SELECT": "선택", + "VIEW": "보기", + "SELECTIONDELETE": "선택 항목 삭제", + "DELETE": "삭제", + "REMOVE": "제거", + "VERIFY": "확인", + "FINISH": "완료", + "FINISHED": "닫기", + "CHANGE": "변경", + "REACTIVATE": "재활성화", + "ACTIVATE": "활성화", + "DEACTIVATE": "비활성화", + "REFRESH": "새로 고침", + "LOGIN": "로그인", + "EDIT": "편집", + "PIN": "고정 / 고정 해제", + "CONFIGURE": "구성", + "SEND": "보내기", + "NEWVALUE": "새로운 값", + "RESTORE": "복원", + "CONTINUEWITHOUTSAVE": "저장하지 않고 계속", + "OF": "의", + "PREVIOUS": "이전", + "NEXT": "다음", + "MORE": "더 보기", + "STEP": "단계", + "SETUP": "설정", + "TEST": "테스트", + "UNSAVEDCHANGES": "저장되지 않은 변경 사항", + "UNSAVED": { + "DIALOG": { + "DESCRIPTION": "이 새 작업을 삭제하시겠습니까? 작업이 손실됩니다.", + "CANCEL": "취소", + "DISCARD": "삭제" + } + }, + "TABLE": { + "SHOWUSER": "사용자 {{value}} 표시" + }, + "DOWNLOAD": "다운로드", + "APPLY": "적용" + }, + "MEMBERROLES": { + "IAM_OWNER": "인스턴스와 모든 조직에 대한 제어 권한이 있습니다", + "IAM_OWNER_VIEWER": "인스턴스와 모든 조직을 검토할 수 있는 권한이 있습니다", + "IAM_ORG_MANAGER": "조직을 생성하고 관리할 수 있는 권한이 있습니다", + "IAM_USER_MANAGER": "사용자를 생성하고 관리할 수 있는 권한이 있습니다", + "IAM_ADMIN_IMPERSONATOR": "모든 조직의 관리자와 최종 사용자를 대리할 수 있는 권한이 있습니다", + "IAM_END_USER_IMPERSONATOR": "모든 조직의 최종 사용자를 대리할 수 있는 권한이 있습니다", + "ORG_OWNER": "조직에 대한 전체 권한이 있습니다", + "ORG_USER_MANAGER": "조직의 사용자를 생성하고 관리할 수 있는 권한이 있습니다", + "ORG_OWNER_VIEWER": "조직 전체를 검토할 수 있는 권한이 있습니다", + "ORG_USER_PERMISSION_EDITOR": "사용자 권한을 관리할 수 있는 권한이 있습니다", + "ORG_PROJECT_PERMISSION_EDITOR": "프로젝트 권한을 관리할 수 있는 권한이 있습니다", + "ORG_PROJECT_CREATOR": "자신의 프로젝트와 하위 설정을 생성할 수 있는 권한이 있습니다", + "ORG_ADMIN_IMPERSONATOR": "조직의 관리자 및 최종 사용자를 대리할 수 있는 권한이 있습니다", + "ORG_END_USER_IMPERSONATOR": "조직의 최종 사용자를 대리할 수 있는 권한이 있습니다", + "PROJECT_OWNER": "프로젝트에 대한 전체 권한이 있습니다", + "PROJECT_OWNER_VIEWER": "프로젝트 전체를 검토할 수 있는 권한이 있습니다", + "PROJECT_OWNER_GLOBAL": "프로젝트에 대한 전체 권한이 있습니다", + "PROJECT_OWNER_VIEWER_GLOBAL": "프로젝트 전체를 검토할 수 있는 권한이 있습니다", + "PROJECT_GRANT_OWNER": "프로젝트 권한 부여를 관리할 수 있는 권한이 있습니다", + "PROJECT_GRANT_OWNER_VIEWER": "프로젝트 권한 부여를 검토할 수 있는 권한이 있습니다" + }, + "OVERLAYS": { + "ORGSWITCHER": { + "TEXT": "콘솔의 모든 조직 설정 및 테이블은 선택된 조직을 기준으로 합니다. 이 버튼을 클릭하여 조직을 변경하거나 새 조직을 생성하세요." + }, + "INSTANCE": { + "TEXT": "기본 설정으로 이동하려면 여기를 클릭하세요. 이 버튼은 향상된 권한이 있는 경우에만 접근할 수 있습니다." + }, + "PROFILE": { + "TEXT": "여기에서 사용자 계정을 전환하고 세션 및 프로필을 관리할 수 있습니다." + }, + "NAV": { + "TEXT": "이 네비게이션은 위에 선택한 조직 또는 인스턴스에 따라 변경됩니다." + }, + "CONTEXTCHANGED": { + "TEXT": "조직 컨텍스트가 변경되었습니다." + }, + "SWITCHEDTOINSTANCE": { + "TEXT": "뷰가 인스턴스로 변경되었습니다!" + } + }, + "FILTER": { + "TITLE": "필터", + "STATE": "상태", + "DISPLAYNAME": "사용자 표시 이름", + "EMAIL": "이메일", + "USERNAME": "사용자 이름", + "ORGNAME": "조직 이름", + "PRIMARYDOMAIN": "기본 도메인", + "PROJECTNAME": "프로젝트 이름", + "RESOURCEOWNER": "리소스 소유자", + "METHODS": { + "5": "포함", + "7": "로 끝남", + "1": "와 일치" + } + }, + "KEYBOARDSHORTCUTS": { + "TITLE": "키보드 단축키", + "UNDERORGCONTEXT": "조직 페이지 내", + "SIDEWIDE": "사이트 전체 단축키", + "SHORTCUTS": { + "HOME": "홈으로 이동 (GH)", + "INSTANCE": "인스턴스로 이동 (GI)", + "ORG": "조직으로 이동 (GO)", + "ORGSETTINGS": "조직 설정으로 이동 (GS)", + "ORGSWITCHER": "조직 변경", + "ME": "내 프로필로 이동", + "PROJECTS": "프로젝트로 이동 (GP)", + "USERS": "사용자로 이동 (GU)", + "USERGRANTS": "인증으로 이동 (GA)", + "ACTIONS": "액션 플로우로 이동 (GF)", + "DOMAINS": "도메인으로 이동 (GD)" + } + }, + "RESOURCEID": "리소스 ID", + "NAME": "Name", + "VERSION": "버전", + "TABLE": { + "NOROWS": "데이터가 없습니다" + }, + "ERRORS": { + "REQUIRED": "이 필드를 입력하세요.", + "ATLEASTONE": "하나 이상의 값을 제공하세요.", + "TOKENINVALID": { + "TITLE": "인증 토큰이 만료되었습니다.", + "DESCRIPTION": "다시 로그인하려면 아래 버튼을 클릭하세요." + }, + "EXHAUSTED": { + "TITLE": "인스턴스가 차단되었습니다.", + "DESCRIPTION": "ZITADEL 인스턴스 관리자에게 구독을 업데이트하도록 요청하세요." + }, + "INVALID_FORMAT": "형식이 유효하지 않습니다.", + "NOTANEMAIL": "입력된 값이 이메일 주소가 아닙니다.", + "MINLENGTH": "{{requiredLength}}자 이상이어야 합니다.", + "MAXLENGTH": "{{requiredLength}}자 이하이어야 합니다.", + "UPPERCASEMISSING": "대문자가 포함되어야 합니다.", + "LOWERCASEMISSING": "소문자가 포함되어야 합니다.", + "SYMBOLERROR": "기호나 구두점이 포함되어야 합니다.", + "NUMBERERROR": "숫자가 포함되어야 합니다.", + "PWNOTEQUAL": "입력된 비밀번호가 일치하지 않습니다.", + "PHONE": "전화번호는 +로 시작해야 합니다." + }, + "USER": { + "SETTINGS": { + "TITLE": "설정", + "GENERAL": "일반", + "IDP": "ID 제공자", + "SECURITY": "비밀번호 및 보안", + "KEYS": "키", + "PAT": "개인 접근 토큰", + "USERGRANTS": "권한 부여", + "MEMBERSHIPS": "멤버십", + "METADATA": "메타데이터" + }, + "TITLE": "개인 정보", + "DESCRIPTION": "정보와 보안 설정을 관리하세요.", + "PAGES": { + "TITLE": "사용자", + "DETAIL": "세부 정보", + "CREATE": "생성", + "MY": "내 정보", + "LOGINNAMES": "로그인 이름", + "LOGINMETHODS": "로그인 방법", + "LOGINNAMESDESC": "다음은 사용자의 로그인 이름입니다:", + "NOUSER": "연관된 사용자가 없습니다.", + "REACTIVATE": "재활성화", + "DEACTIVATE": "비활성화", + "FILTER": "필터", + "STATE": "상태", + "DELETE": "사용자 삭제", + "UNLOCK": "사용자 잠금 해제", + "GENERATESECRET": "클라이언트 시크릿 생성", + "REMOVESECRET": "클라이언트 시크릿 삭제", + "LOCKEDDESCRIPTION": "로그인 시도 횟수가 초과되어 사용자가 잠금되었습니다. 사용하려면 잠금을 해제해야 합니다.", + "DELETEACCOUNT": "계정 삭제", + "DELETEACCOUNT_DESC": "이 작업을 수행하면 로그아웃되며 계정에 다시 접근할 수 없습니다. 이 작업은 되돌릴 수 없으므로 신중히 진행하세요.", + "DELETEACCOUNT_BTN": "계정 삭제", + "DELETEACCOUNT_SUCCESS": "계정이 성공적으로 삭제되었습니다!" + }, + "DETAILS": { + "DATECREATED": "생성일", + "DATECHANGED": "수정일" + }, + "DIALOG": { + "DELETE_TITLE": "사용자 삭제", + "DELETE_SELF_TITLE": "계정 삭제", + "DELETE_DESCRIPTION": "사용자를 영구 삭제하려고 합니다. 정말로 진행하시겠습니까?", + "DELETE_SELF_DESCRIPTION": "개인 계정을 영구 삭제하려고 합니다. 이 작업은 사용자를 로그아웃하고 계정을 삭제합니다. 이 작업은 되돌릴 수 없습니다!", + "DELETE_AUTH_DESCRIPTION": "개인 계정을 영구 삭제하려고 합니다. 정말로 진행하시겠습니까?", + "TYPEUSERNAME": "'{{value}}'을(를) 입력하여 사용자를 삭제하세요.", + "USERNAME": "로그인 이름", + "DELETE_BTN": "영구 삭제" + }, + "SENDEMAILDIALOG": { + "TITLE": "이메일 알림 보내기", + "DESCRIPTION": "현재 이메일 주소로 알림을 보내려면 아래 버튼을 클릭하거나 필드에서 이메일 주소를 변경하세요.", + "NEWEMAIL": "새 이메일 주소" + }, + "SECRETDIALOG": { + "CLIENTSECRET": "클라이언트 시크릿", + "CLIENTSECRET_DESCRIPTION": "클라이언트 시크릿은 안전한 장소에 보관하세요. 이 대화 상자를 닫으면 다시 볼 수 없습니다." + }, + "TABLE": { + "DEACTIVATE": "비활성화", + "ACTIVATE": "활성화", + "CHANGEDATE": "마지막 수정", + "CREATIONDATE": "생성일", + "FILTER": { + "0": "표시 이름으로 필터링", + "1": "사용자 이름으로 필터링", + "2": "표시 이름으로 필터링", + "3": "사용자 이름으로 필터링", + "4": "이메일로 필터링", + "5": "표시 이름으로 필터링", + "10": "조직 이름으로 필터링", + "12": "프로젝트 이름으로 필터링" + }, + "EMPTY": "항목 없음" + }, + "PASSWORDLESS": { + "SEND": "등록 링크 보내기", + "TABLETYPE": "유형", + "TABLESTATE": "상태", + "NAME": "이름", + "EMPTY": "설정된 장치 없음", + "TITLE": "비밀번호 없는 인증", + "DESCRIPTION": "ZITADEL에 비밀번호 없이 로그인할 수 있도록 WebAuthn 기반 인증 방법을 추가하세요.", + "MANAGE_DESCRIPTION": "사용자의 두 번째 인증 요소를 관리하세요.", + "U2F": "방법 추가", + "U2F_DIALOG_TITLE": "인증기 확인", + "U2F_DIALOG_DESCRIPTION": "비밀번호 없는 로그인에 사용할 이름을 입력하세요.", + "U2F_SUCCESS": "비밀번호 없는 인증이 성공적으로 생성되었습니다!", + "U2F_ERROR": "설정 중 오류가 발생했습니다!", + "U2F_NAME": "인증기 이름", + "TYPE": { + "0": "다중 인증 미정의", + "1": "일회용 비밀번호 (OTP)", + "2": "지문, 보안 키, Face ID 및 기타" + }, + "STATE": { + "0": "상태 없음", + "1": "준비되지 않음", + "2": "준비됨", + "3": "삭제됨" + }, + "DIALOG": { + "DELETE_TITLE": "비밀번호 없는 인증 방법 제거", + "DELETE_DESCRIPTION": "비밀번호 없는 인증 방법을 삭제하려고 합니다. 진행하시겠습니까?", + "ADD_TITLE": "비밀번호 없는 인증", + "ADD_DESCRIPTION": "비밀번호 없는 인증 방법을 만들기 위해 사용할 수 있는 옵션 중 하나를 선택하세요.", + "SEND_DESCRIPTION": "이메일 주소로 등록 링크를 보내세요.", + "SEND": "등록 링크 보내기", + "SENT": "이메일이 성공적으로 발송되었습니다. 메일함을 확인하여 설정을 계속 진행하세요.", + "QRCODE_DESCRIPTION": "다른 장치로 스캔할 QR 코드를 생성하세요.", + "QRCODE": "QR 코드 생성", + "QRCODE_SCAN": "설정을 계속하려면 이 QR 코드를 스캔하세요.", + "NEW_DESCRIPTION": "이 장치를 사용하여 비밀번호 없는 인증을 설정하세요.", + "NEW": "새로 추가" + } + }, + "MFA": { + "TABLETYPE": "유형", + "TABLESTATE": "상태", + "NAME": "이름", + "EMPTY": "추가 인증 요소 없음", + "TITLE": "다중 인증", + "DESCRIPTION": "계정의 보안을 위해 두 번째 인증 요소를 추가하세요.", + "MANAGE_DESCRIPTION": "사용자의 두 번째 인증 방법을 관리하세요.", + "ADD": "인증 요소 추가", + "OTP": "TOTP (시간 기반 일회용 비밀번호)용 인증 앱", + "OTP_DIALOG_TITLE": "OTP 추가", + "OTP_DIALOG_DESCRIPTION": "인증 앱으로 QR 코드를 스캔하고, 아래에 코드를 입력하여 OTP 방법을 검증하고 활성화하세요.", + "U2F": "지문, 보안 키, Face ID 및 기타", + "U2F_DIALOG_TITLE": "인증 요소 확인", + "U2F_DIALOG_DESCRIPTION": "사용할 다중 인증의 이름을 입력하세요.", + "U2F_SUCCESS": "인증 요소가 성공적으로 추가되었습니다!", + "U2F_ERROR": "설정 중 오류가 발생했습니다!", + "U2F_NAME": "인증기 이름", + "OTPSMS": "SMS를 통한 OTP (일회용 비밀번호)", + "OTPEMAIL": "이메일을 통한 OTP (일회용 비밀번호)", + "SETUPOTPSMSDESCRIPTION": "이 전화번호를 OTP (일회용 비밀번호) 두 번째 인증 요소로 설정하시겠습니까?", + "OTPSMSSUCCESS": "OTP 인증 요소가 성공적으로 설정되었습니다.", + "OTPSMSPHONEMUSTBEVERIFIED": "이 방법을 사용하려면 전화번호를 확인해야 합니다.", + "OTPEMAILSUCCESS": "OTP 인증 요소가 성공적으로 설정되었습니다.", + "TYPE": { + "0": "다중 인증 미정의", + "1": "일회용 비밀번호 (OTP)", + "2": "지문, 보안 키, Face ID 및 기타" + }, + "STATE": { + "0": "상태 없음", + "1": "준비되지 않음", + "2": "준비됨", + "3": "삭제됨" + }, + "DIALOG": { + "MFA_DELETE_TITLE": "두 번째 인증 요소 제거", + "MFA_DELETE_DESCRIPTION": "두 번째 인증 요소를 삭제하려고 합니다. 정말로 진행하시겠습니까?", + "ADD_MFA_TITLE": "두 번째 인증 요소 추가", + "ADD_MFA_DESCRIPTION": "다음 옵션 중 하나를 선택하세요." + } + }, + "EXTERNALIDP": { + "TITLE": "외부 ID 제공자", + "DESC": "", + "IDPCONFIGID": "ID 제공자 구성 ID", + "IDPNAME": "ID 제공자 이름", + "USERDISPLAYNAME": "외부 이름", + "EXTERNALUSERID": "외부 사용자 ID", + "EMPTY": "외부 ID 제공자를 찾을 수 없습니다", + "DIALOG": { + "DELETE_TITLE": "ID 제공자 제거", + "DELETE_DESCRIPTION": "사용자로부터 ID 제공자를 삭제하려고 합니다. 계속하시겠습니까?" + } + }, + "CREATE": { + "TITLE": "새 사용자 생성", + "DESCRIPTION": "필요한 정보를 입력하세요.", + "NAMEANDEMAILSECTION": "이름과 이메일", + "GENDERLANGSECTION": "성별과 언어", + "PHONESECTION": "전화번호", + "PASSWORDSECTION": "초기 비밀번호", + "ADDRESSANDPHONESECTION": "전화번호", + "INITMAILDESCRIPTION": "두 옵션이 모두 선택된 경우 초기화 이메일이 전송되지 않습니다. 하나의 옵션만 선택된 경우 데이터 제공/확인을 위한 이메일이 전송됩니다." + }, + "CODEDIALOG": { + "TITLE": "전화번호 확인", + "DESCRIPTION": "전화번호를 확인하려면 문자 메시지로 받은 코드를 입력하세요.", + "CODE": "코드" + }, + "DATA": { + "STATE": "상태", + "STATE0": "알 수 없음", + "STATE1": "활성", + "STATE2": "비활성", + "STATE3": "삭제됨", + "STATE4": "잠김", + "STATE5": "일시 중단됨", + "STATE6": "초기" + }, + "PROFILE": { + "TITLE": "프로필", + "EMAIL": "이메일", + "PHONE": "전화번호", + "PHONE_HINT": "+ 기호 다음에 국가 코드를 입력하거나 드롭다운에서 국가를 선택한 후 전화번호를 입력하세요.", + "USERNAME": "사용자 이름", + "CHANGEUSERNAME": "수정", + "CHANGEUSERNAME_TITLE": "사용자 이름 변경", + "CHANGEUSERNAME_DESC": "아래 필드에 새 이름을 입력하세요.", + "FIRSTNAME": "이름", + "LASTNAME": "성", + "NICKNAME": "별명", + "DISPLAYNAME": "표시 이름", + "PREFERREDLOGINNAME": "선호 로그인 이름", + "PREFERRED_LANGUAGE": "언어", + "GENDER": "성별", + "PASSWORD": "비밀번호", + "AVATAR": { + "UPLOADTITLE": "프로필 사진 업로드", + "UPLOADBTN": "파일 선택", + "UPLOAD": "업로드", + "CURRENT": "현재 사진", + "PREVIEW": "미리보기", + "DELETESUCCESS": "성공적으로 삭제되었습니다!", + "CROPPERERROR": "파일 업로드 중 오류가 발생했습니다. 필요 시 다른 형식과 크기를 시도하세요." + }, + "COUNTRY": "국가" + }, + "MACHINE": { + "TITLE": "서비스 사용자 세부 정보", + "USERNAME": "사용자 이름", + "NAME": "이름", + "DESCRIPTION": "설명", + "KEYSTITLE": "키", + "KEYSDESC": "키를 정의하고 만료 날짜를 선택적으로 추가하세요.", + "TOKENSTITLE": "개인 접근 토큰", + "TOKENSDESC": "개인 접근 토큰은 일반적인 OAuth 접근 토큰과 유사하게 작동합니다.", + "ID": "키 ID", + "TYPE": "유형", + "EXPIRATIONDATE": "만료 날짜", + "CHOOSEDATEAFTER": "유효한 만료 날짜를 선택하세요", + "CHOOSEEXPIRY": "만료 날짜 선택", + "CREATIONDATE": "생성일", + "KEYDETAILS": "키 세부 정보", + "ACCESSTOKENTYPE": "접근 토큰 유형", + "ACCESSTOKENTYPES": { + "0": "Bearer", + "1": "JWT" + }, + "ADD": { + "TITLE": "키 추가", + "DESCRIPTION": "키 유형을 선택하고 만료 날짜를 선택하세요." + }, + "ADDED": { + "TITLE": "키가 생성되었습니다", + "DESCRIPTION": "키를 다운로드하세요. 이 대화 상자를 닫으면 다시 볼 수 없습니다!" + }, + "KEYTYPES": { + "1": "JSON" + }, + "DIALOG": { + "DELETE_KEY": { + "TITLE": "키 삭제", + "DESCRIPTION": "선택한 키를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다." + } + } + }, + "PASSWORD": { + "TITLE": "비밀번호", + "LABEL": "안전한 비밀번호는 계정 보호에 도움이 됩니다", + "DESCRIPTION": "아래 정책에 따라 새 비밀번호를 입력하세요.", + "OLD": "현재 비밀번호", + "NEW": "새 비밀번호", + "CONFIRM": "새 비밀번호 확인", + "NEWINITIAL": "비밀번호", + "CONFIRMINITIAL": "비밀번호 확인", + "RESET": "현재 비밀번호 재설정", + "SET": "새 비밀번호 설정", + "RESENDNOTIFICATION": "비밀번호 재설정 링크 보내기", + "REQUIRED": "필수 필드가 누락되었습니다.", + "MINLENGTHERROR": "{{value}}자 이상이어야 합니다.", + "MAXLENGTHERROR": "{{value}}자 이하이어야 합니다." + }, + "ID": "ID", + "EMAIL": "이메일", + "PHONE": "전화번호", + "PHONEEMPTY": "정의된 전화번호가 없습니다", + "PHONEVERIFIED": "전화번호가 확인되었습니다.", + "EMAILVERIFIED": "이메일이 확인되었습니다", + "NOTVERIFIED": "확인되지 않음", + "PREFERRED_LOGINNAME": "선호 로그인 이름", + "ISINITIAL": "사용자가 아직 활성화되지 않았습니다.", + "LOGINMETHODS": { + "TITLE": "연락처 정보", + "DESCRIPTION": "제공된 정보는 비밀번호 재설정 이메일 등의 중요한 정보를 전송하는 데 사용됩니다.", + "EMAIL": { + "TITLE": "이메일", + "VALID": "확인됨", + "ISVERIFIED": "이메일 확인됨", + "ISVERIFIEDDESC": "이메일이 확인된 것으로 표시되면 이메일 확인 요청이 보내지지 않습니다.", + "RESEND": "이메일 확인 요청 다시 보내기", + "EDITTITLE": "이메일 변경", + "EDITDESC": "새 이메일을 아래 필드에 입력하세요." + }, + "PHONE": { + "TITLE": "전화", + "VALID": "확인됨", + "RESEND": "인증 문자 다시 보내기", + "EDITTITLE": "번호 변경", + "EDITVALUE": "전화번호", + "EDITDESC": "새 전화번호를 아래 필드에 입력하세요.", + "DELETETITLE": "전화번호 삭제", + "DELETEDESC": "전화번호를 정말로 삭제하시겠습니까?", + "OTPSMSREMOVALWARNING": "이 계정은 이 전화번호를 두 번째 인증 요소로 사용 중입니다. 삭제 후에는 사용할 수 없습니다." + }, + "RESENDCODE": "코드 다시 보내기", + "ENTERCODE": "확인", + "ENTERCODE_DESC": "코드 확인" + }, + "GRANTS": { + "TITLE": "사용자 권한", + "DESCRIPTION": "특정 프로젝트에 대한 이 사용자의 접근을 부여하세요", + "CREATE": { + "TITLE": "사용자 권한 생성", + "DESCRIPTION": "조직, 프로젝트 및 관련 프로젝트 역할을 검색하세요." + }, + "PROJECTNAME": "프로젝트 이름", + "PROJECT-OWNED": "프로젝트", + "PROJECT-GRANTED": "부여된 프로젝트", + "FILTER": { + "0": "사용자로 필터링", + "1": "도메인으로 필터링", + "2": "프로젝트 이름으로 필터링", + "3": "역할 이름으로 필터링" + } + }, + "STATE": { + "0": "알 수 없음", + "1": "활성", + "2": "비활성", + "3": "삭제됨", + "4": "잠김", + "5": "일시 중단됨", + "6": "초기" + }, + "SEARCH": { + "ADDITIONAL": "로그인 이름 (현재 조직)", + "ADDITIONAL-EXTERNAL": "로그인 이름 (외부 조직)" + }, + "TARGET": { + "SELF": "다른 조직의 사용자에게 권한을 부여하려면", + "EXTERNAL": "조직의 사용자에게 권한을 부여하려면", + "CLICKHERE": "여기를 클릭하세요" + }, + "SIGNEDOUT": "로그아웃되었습니다. 다시 로그인하려면 '로그인' 버튼을 클릭하세요.", + "SIGNEDOUT_BTN": "로그인", + "EDITACCOUNT": "계정 편집", + "ADDACCOUNT": "다른 계정으로 로그인", + "RESENDINITIALEMAIL": "활성화 이메일 다시 보내기", + "RESENDEMAILNOTIFICATION": "이메일 알림 다시 보내기", + "TOAST": { + "CREATED": "사용자가 성공적으로 생성되었습니다.", + "SAVED": "프로필이 성공적으로 저장되었습니다.", + "USERNAMECHANGED": "사용자 이름이 변경되었습니다.", + "EMAILSAVED": "이메일이 성공적으로 저장되었습니다.", + "INITEMAILSENT": "초기화 이메일이 전송되었습니다.", + "PHONESAVED": "전화번호가 성공적으로 저장되었습니다.", + "PHONEREMOVED": "전화번호가 제거되었습니다.", + "PHONEVERIFIED": "전화번호가 성공적으로 확인되었습니다.", + "PHONEVERIFICATIONSENT": "전화번호 확인 코드가 전송되었습니다.", + "EMAILVERIFICATIONSENT": "이메일 확인 코드가 전송되었습니다.", + "OTPREMOVED": "OTP가 제거되었습니다.", + "U2FREMOVED": "인증 요소가 제거되었습니다.", + "PASSWORDLESSREMOVED": "비밀번호 없는 인증이 제거되었습니다.", + "INITIALPASSWORDSET": "초기 비밀번호가 설정되었습니다.", + "PASSWORDNOTIFICATIONSENT": "비밀번호 변경 알림이 전송되었습니다.", + "PASSWORDCHANGED": "비밀번호가 성공적으로 변경되었습니다.", + "REACTIVATED": "사용자가 재활성화되었습니다.", + "DEACTIVATED": "사용자가 비활성화되었습니다.", + "SELECTEDREACTIVATED": "선택된 사용자가 재활성화되었습니다.", + "SELECTEDDEACTIVATED": "선택된 사용자가 비활성화되었습니다.", + "SELECTEDKEYSDELETED": "선택된 키가 삭제되었습니다.", + "KEYADDED": "키가 추가되었습니다!", + "MACHINEADDED": "서비스 사용자가 생성되었습니다!", + "DELETED": "사용자가 성공적으로 삭제되었습니다!", + "UNLOCKED": "사용자가 성공적으로 잠금 해제되었습니다!", + "PASSWORDLESSREGISTRATIONSENT": "등록 링크가 성공적으로 전송되었습니다.", + "SECRETGENERATED": "시크릿이 성공적으로 생성되었습니다!", + "SECRETREMOVED": "시크릿이 성공적으로 제거되었습니다!" + }, + "MEMBERSHIPS": { + "TITLE": "ZITADEL 관리자 역할", + "DESCRIPTION": "이 사용자의 모든 멤버 권한입니다. 조직, 프로젝트 또는 IAM 세부 페이지에서 수정할 수도 있습니다.", + "ORGCONTEXT": "현재 선택된 조직과 관련된 모든 조직과 프로젝트를 볼 수 있습니다.", + "USERCONTEXT": "사용 권한이 있는 모든 조직과 프로젝트를 볼 수 있습니다. 다른 조직도 포함됩니다.", + "CREATIONDATE": "생성 날짜", + "CHANGEDATE": "마지막 수정", + "DISPLAYNAME": "표시 이름", + "REMOVE": "제거", + "TYPE": "유형", + "ORGID": "조직 ID", + "UPDATED": "멤버십이 업데이트되었습니다.", + "NOPERMISSIONTOEDIT": "역할을 편집할 권한이 없습니다!", + "TYPES": { + "UNKNOWN": "알 수 없음", + "ORG": "조직", + "PROJECT": "프로젝트", + "GRANTEDPROJECT": "부여된 프로젝트" + } + }, + "PERSONALACCESSTOKEN": { + "ID": "ID", + "TOKEN": "토큰", + "ADD": { + "TITLE": "새로운 개인 액세스 토큰 생성", + "DESCRIPTION": "토큰의 사용자 정의 만료일을 설정하세요.", + "CHOOSEEXPIRY": "만료 날짜 선택", + "CHOOSEDATEAFTER": "유효한 만료일 입력" + }, + "ADDED": { + "TITLE": "개인 액세스 토큰", + "DESCRIPTION": "개인 액세스 토큰을 복사하세요. 다시 볼 수 없습니다!" + }, + "DELETE": { + "TITLE": "토큰 삭제", + "DESCRIPTION": "개인 액세스 토큰을 삭제하려고 합니다. 확실합니까?" + }, + "DELETED": "토큰이 성공적으로 삭제되었습니다." + } + }, + "METADATA": { + "TITLE": "메타데이터", + "KEY": "키", + "VALUE": "값", + "ADD": "새 항목", + "SAVE": "저장", + "EMPTY": "메타데이터 없음", + "SETSUCCESS": "항목이 성공적으로 저장되었습니다", + "REMOVESUCCESS": "항목이 성공적으로 삭제되었습니다" + }, + "FLOWS": { + "ID": "ID", + "NAME": "이름", + "STATE": "상태", + "STATES": { + "0": "상태 없음", + "1": "비활성", + "2": "활성" + }, + "ADDTRIGGER": "트리거 추가", + "FLOWCHANGED": "플로우가 성공적으로 변경되었습니다", + "FLOWCLEARED": "플로우가 성공적으로 초기화되었습니다", + "TIMEOUT": "시간 초과", + "TIMEOUTINSEC": "초 단위 시간 초과", + "ALLOWEDTOFAIL": "실패 허용", + "ALLOWEDTOFAILWARN": { + "TITLE": "경고", + "DESCRIPTION": "이 설정을 비활성화하면 조직 내 사용자가 로그인할 수 없게 될 수 있습니다. 또한, 콘솔에 다시 접근하여 작업을 비활성화할 수 없게 됩니다. 별도의 조직에서 관리자 계정을 만들거나 개발 환경 또는 개발 조직에서 스크립트를 먼저 테스트할 것을 권장합니다." + }, + "SCRIPT": "스크립트", + "FLOWTYPE": "플로우 유형", + "TRIGGERTYPE": "트리거 유형", + "ACTIONS": "작업", + "ACTIONSMAX": "사용 가능한 작업의 수는 티어에 따라 제한됩니다 ({{value}}). 필요하지 않은 작업은 비활성화하거나 티어 업그레이드를 고려하세요.", + "DIALOG": { + "ADD": { + "TITLE": "작업 생성" + }, + "UPDATE": { + "TITLE": "작업 업데이트" + }, + "DELETEACTION": { + "TITLE": "작업 삭제?", + "DESCRIPTION": "작업을 삭제하려고 합니다. 이 작업은 되돌릴 수 없습니다. 확실합니까?", + "DELETE_SUCCESS": "작업이 성공적으로 삭제되었습니다." + }, + "CLEAR": { + "TITLE": "플로우 초기화?", + "DESCRIPTION": "트리거 및 작업과 함께 플로우를 초기화하려고 합니다. 이 변경 사항은 복구할 수 없습니다. 확실합니까?" + }, + "REMOVEACTIONSLIST": { + "TITLE": "선택한 작업 삭제?", + "DESCRIPTION": "선택한 작업을 플로우에서 삭제하시겠습니까?" + }, + "ABOUTNAME": "작업 이름과 자바스크립트 함수 이름은 동일해야 합니다" + }, + "TOAST": { + "ACTIONSSET": "작업이 설정되었습니다", + "ACTIONREACTIVATED": "작업이 성공적으로 재활성화되었습니다", + "ACTIONDEACTIVATED": "작업이 성공적으로 비활성화되었습니다" + } + }, + "IAM": { + "POLICIES": { + "TITLE": "시스템 정책 및 액세스 설정", + "DESCRIPTION": "전역 정책 및 관리 액세스 설정을 관리합니다." + }, + "EVENTSTORE": { + "TITLE": "IAM 스토리지 관리", + "DESCRIPTION": "ZITADEL 뷰 및 실패한 이벤트를 관리합니다." + }, + "MEMBER": { + "TITLE": "관리자", + "DESCRIPTION": "이 관리자들은 인스턴스 내에서 변경할 권한이 있습니다." + }, + "PAGES": { + "STATE": "상태", + "DOMAINLIST": "사용자 정의 도메인" + }, + "STATE": { + "0": "미지정", + "1": "생성 중", + "2": "실행 중", + "3": "중지 중", + "4": "중지됨" + }, + "VIEWS": { + "VIEWNAME": "이름", + "DATABASE": "데이터베이스", + "SEQUENCE": "순서", + "EVENTTIMESTAMP": "타임스탬프", + "LASTSPOOL": "성공적 스풀", + "ACTIONS": "작업", + "CLEAR": "지우기", + "CLEARED": "뷰가 성공적으로 지워졌습니다!", + "DIALOG": { + "VIEW_CLEAR_TITLE": "뷰 지우기", + "VIEW_CLEAR_DESCRIPTION": "뷰를 지우려 하고 있습니다. 뷰를 지우면 데이터가 최종 사용자에게 사용 불가한 상태가 될 수 있습니다. 정말 확실합니까?" + } + }, + "FAILEDEVENTS": { + "VIEWNAME": "이름", + "DATABASE": "데이터베이스", + "FAILEDSEQUENCE": "실패한 순서", + "FAILURECOUNT": "실패 횟수", + "LASTFAILED": "마지막 실패 시점", + "ERRORMESSAGE": "오류 메시지", + "ACTIONS": "작업", + "DELETE": "삭제", + "DELETESUCCESS": "실패한 이벤트가 삭제되었습니다." + }, + "EVENTS": { + "EDITOR": "편집자", + "EDITORID": "편집자 ID", + "AGGREGATE": "집합", + "AGGREGATEID": "집합 ID", + "AGGREGATETYPE": "집합 유형", + "RESOURCEOWNER": "리소스 소유자", + "SEQUENCE": "순서", + "CREATIONDATE": "생성일", + "TYPE": "유형", + "PAYLOAD": "페이로드", + "FILTERS": { + "BTN": "필터", + "USER": { + "IDLABEL": "ID", + "CHECKBOX": "편집자로 필터" + }, + "AGGREGATE": { + "TYPELABEL": "집합 유형", + "IDLABEL": "ID", + "CHECKBOX": "집합으로 필터" + }, + "TYPE": { + "TYPELABEL": "유형", + "CHECKBOX": "유형으로 필터" + }, + "RESOURCEOWNER": { + "LABEL": "ID", + "CHECKBOX": "리소스 소유자로 필터" + }, + "SEQUENCE": { + "LABEL": "순서", + "CHECKBOX": "순서로 필터" + }, + "SORT": "정렬", + "ASC": "오름차순", + "DESC": "내림차순", + "CREATIONDATE": { + "RADIO_FROM": "부터", + "RADIO_RANGE": "범위", + "LABEL_SINCE": "이후", + "LABEL_UNTIL": "까지" + }, + "OTHER": "기타", + "OTHERS": "기타들" + }, + "DIALOG": { + "TITLE": "이벤트 상세 정보" + } + }, + "TOAST": { + "MEMBERREMOVED": "관리자가 제거되었습니다.", + "MEMBERSADDED": "관리자가 추가되었습니다.", + "MEMBERADDED": "관리자가 추가되었습니다.", + "MEMBERCHANGED": "관리자가 변경되었습니다.", + "ROLEREMOVED": "역할이 제거되었습니다.", + "ROLECHANGED": "역할이 변경되었습니다.", + "REACTIVATED": "재활성화됨", + "DEACTIVATED": "비활성화됨" + } + }, + "ORG": { + "PAGES": { + "NAME": "이름", + "ID": "ID", + "CREATIONDATE": "생성일", + "DATECHANGED": "변경일", + "FILTER": "필터", + "FILTERPLACEHOLDER": "이름으로 필터링", + "LIST": "조직", + "LISTDESCRIPTION": "조직을 선택하세요.", + "ACTIVE": "활성", + "CREATE": "조직 생성", + "DEACTIVATE": "조직 비활성화", + "REACTIVATE": "조직 재활성화", + "NOPERMISSION": "조직 설정에 접근할 권한이 없습니다.", + "USERSELFACCOUNT": "개인 계정을 조직 소유자로 사용", + "ORGDETAIL_TITLE": "새 조직의 이름과 도메인을 입력하세요.", + "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "새 조직의 이름을 입력하세요.", + "ORGDETAILUSER_TITLE": "조직 소유자 설정", + "DELETE": "조직 삭제", + "DEFAULTLABEL": "기본", + "SETASDEFAULT": "기본 조직으로 설정", + "DEFAULTORGSET": "기본 조직이 성공적으로 변경되었습니다", + "RENAME": { + "ACTION": "이름 변경", + "TITLE": "조직 이름 변경", + "DESCRIPTION": "새 조직 이름을 입력하세요", + "BTN": "이름 변경" + }, + "ORGDOMAIN": { + "TITLE": "{{value}} 소유권 확인", + "VERIFICATION": "도메인을 수동으로 검증할 수 있는 두 가지 방법을 제공합니다:", + "VERIFICATION_HTML": "- HTTP. 웹사이트에 임시 검증 파일을 호스팅하세요", + "VERIFICATION_DNS": "- DNS. TXT 레코드 DNS 항목을 생성하세요", + "VERIFICATION_DNS_DESC": "{{value}}을 관리하고 DNS 기록에 접근할 수 있다면, 다음 값을 사용하여 새 TXT 레코드를 생성할 수 있습니다:", + "VERIFICATION_DNS_HOST_LABEL": "호스트:", + "VERIFICATION_DNS_CHALLENGE_LABEL": "TXT 레코드 값으로 이 코드를 사용하세요:", + "VERIFICATION_HTTP_DESC": "웹사이트 호스팅에 접근할 수 있다면, 검증 파일을 다운로드하고 제공된 URL에 업로드하세요", + "VERIFICATION_HTTP_URL_LABEL": "예상 URL:", + "VERIFICATION_HTTP_FILE_LABEL": "검증 파일:", + "VERIFICATION_SKIP": "지금은 검증을 건너뛰고 조직 생성을 계속할 수 있지만, 도메인을 사용하려면 이 단계를 완료해야 합니다!", + "VERIFICATION_VALIDATION_DESC": "검증 코드를 삭제하지 마세요. ZITADEL은 주기적으로 도메인 소유권을 다시 확인할 것입니다.", + "VERIFICATION_NEWTOKEN_TITLE": "새 토큰 요청", + "VERIFICATION_VALIDATION_ONGOING": "{{value}} 방법이 도메인 검증을 위해 선택되었습니다. 검증 검사를 실행하거나 검증 프로세스를 재설정하려면 버튼을 클릭하세요.", + "VERIFICATION_SUCCESSFUL": "도메인이 성공적으로 검증되었습니다!", + "RESETMETHOD": "검증 방법 재설정" + }, + "DOWNLOAD_FILE": "파일 다운로드", + "SELECTORGTOOLTIP": "이 조직을 선택하세요.", + "PRIMARYDOMAIN": "기본 도메인", + "STATE": "상태", + "USEPASSWORD": "초기 비밀번호 설정", + "USEPASSWORDDESC": "초기화 중에 사용자가 비밀번호를 설정할 필요가 없습니다." + }, + "LIST": { + "TITLE": "조직", + "DESCRIPTION": "이 인스턴스의 조직들" + }, + "DOMAINS": { + "NEW": "도메인 추가", + "TITLE": "검증된 도메인", + "DESCRIPTION": "조직 도메인을 구성하세요. 이 도메인은 도메인 검색 및 사용자 이름 접미사에 사용할 수 있습니다.", + "SETPRIMARY": "기본으로 설정", + "DELETE": { + "TITLE": "도메인 삭제", + "DESCRIPTION": "도메인 중 하나를 삭제하려고 합니다." + }, + "ADD": { + "TITLE": "도메인 추가", + "DESCRIPTION": "조직에 도메인을 추가하려고 합니다. 프로세스가 성공하면, 이 도메인은 도메인 검색 및 사용자 접미사로 사용할 수 있습니다." + } + }, + "STATE": { + "0": "미정", + "1": "활성", + "2": "비활성화됨" + }, + "MEMBER": { + "TITLE": "조직 관리자", + "DESCRIPTION": "조직 설정을 변경할 수 있는 사용자를 정의하세요." + }, + "TOAST": { + "UPDATED": "조직이 성공적으로 업데이트되었습니다.", + "DEACTIVATED": "조직이 비활성화되었습니다.", + "REACTIVATED": "조직이 재활성화되었습니다.", + "DOMAINADDED": "도메인이 추가되었습니다.", + "DOMAINREMOVED": "도메인이 삭제되었습니다.", + "MEMBERADDED": "관리자가 추가되었습니다.", + "MEMBERREMOVED": "관리자가 제거되었습니다.", + "MEMBERCHANGED": "관리자가 변경되었습니다.", + "SETPRIMARY": "기본 도메인이 설정되었습니다.", + "DELETED": "조직이 성공적으로 삭제되었습니다", + "DEFAULTORGNOTFOUND": "기본 조직을 찾을 수 없습니다", + "ORG_WAS_DELETED": "조직이 삭제되었습니다." + }, + "DIALOG": { + "DEACTIVATE": { + "TITLE": "조직 비활성화", + "DESCRIPTION": "조직을 비활성화하려고 합니다. 이후에는 사용자가 로그인할 수 없습니다. 계속 진행하시겠습니까?" + }, + "REACTIVATE": { + "TITLE": "조직 재활성화", + "DESCRIPTION": "조직을 재활성화하려고 합니다. 사용자가 다시 로그인할 수 있게 됩니다. 계속 진행하시겠습니까?" + }, + "DELETE": { + "TITLE": "조직 삭제", + "DESCRIPTION": "조직을 삭제하려고 합니다. 이 작업은 조직과 관련된 모든 데이터를 삭제하는 과정을 시작합니다. 현재로서는 이 작업을 되돌릴 수 없습니다.", + "TYPENAME": "조직을 삭제하려면 '{{value}}'을(를) 입력하세요.", + "ORGNAME": "이름", + "BTN": "삭제" + } + } + }, + "SETTINGS": { + "LIST": { + "ORGS": "조직", + "FEATURESETTINGS": "기능 설정", + "LANGUAGES": "언어", + "LOGIN": "로그인 동작 및 보안", + "LOCKOUT": "잠금", + "AGE": "비밀번호 만료", + "COMPLEXITY": "비밀번호 복잡성", + "NOTIFICATIONS": "알림", + "SMTP_PROVIDER": "SMTP 제공자", + "SMS_PROVIDER": "SMS/전화 제공자", + "NOTIFICATIONS_DESC": "SMTP 및 SMS 설정", + "MESSAGETEXTS": "메시지 텍스트", + "IDP": "ID 제공자", + "VERIFIED_DOMAINS": "검증된 도메인", + "DOMAIN": "도메인 설정", + "LOGINTEXTS": "로그인 인터페이스 텍스트", + "BRANDING": "브랜딩", + "PRIVACYPOLICY": "외부 링크", + "OIDC": "OIDC 토큰 수명 및 만료", + "SECRETS": "시크릿 생성기", + "SECURITY": "보안 설정", + "EVENTS": "이벤트", + "FAILEDEVENTS": "실패한 이벤트", + "VIEWS": "뷰" + }, + "GROUPS": { + "GENERAL": "일반 정보", + "NOTIFICATIONS": "알림", + "LOGIN": "로그인 및 접근", + "DOMAIN": "도메인", + "TEXTS": "텍스트 및 언어", + "APPEARANCE": "외형", + "OTHER": "기타", + "STORAGE": "저장소" + } + }, + "SETTING": { + "LANGUAGES": { + "DEFAULT": "기본 언어", + "ALLOWED": "허용하는 언어", + "NOT_ALLOWED": "허용하지 않는 언어", + "ALLOW_ALL": "모두 허용", + "DISALLOW_ALL": "모두 비허용", + "SETASDEFAULT": "기본 언어로 설정", + "DEFAULT_SAVED": "기본 언어가 저장되었습니다.", + "ALLOWED_SAVED": "허용하는 언어가 저장되었습니다.", + "OPTIONS": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски", + "cs": "Čeština", + "ru": "Русский", + "nl": "Nederlands", + "sv": "Svenska", + "id": "Bahasa Indonesia", + "hu": "Magyar", + "ko": "한국어" + } + }, + "SMTP": { + "TITLE": "SMTP 제공자", + "DESCRIPTION": "설명", + "SENDERADDRESS": "발신 이메일 주소", + "SENDERNAME": "발신자 이름", + "REPLYTOADDRESS": "회신 주소", + "HOSTANDPORT": "호스트 및 포트", + "USER": "사용자", + "PASSWORD": "비밀번호", + "SETPASSWORD": "SMTP 비밀번호 설정", + "PASSWORDSET": "SMTP 비밀번호가 성공적으로 설정되었습니다.", + "TLS": "전송 계층 보안 (TLS)", + "SAVED": "성공적으로 저장되었습니다!", + "NOCHANGES": "변경 사항이 없습니다!", + "REQUIREDWARN": "도메인에서 알림을 보내려면 SMTP 데이터를 입력해야 합니다." + }, + "SMS": { + "PROVIDERS": "제공자", + "PROVIDER": "SMS 제공자", + "ADDPROVIDER": "SMS 제공자 추가", + "ADDPROVIDERDESCRIPTION": "사용 가능한 제공자 중 하나를 선택하고 필요한 데이터를 입력하세요.", + "REMOVEPROVIDER": "제공자 제거", + "REMOVEPROVIDER_DESC": "제공자 구성을 삭제하려고 합니다. 계속하시겠습니까?", + "SMSPROVIDERSTATE": { + "0": "미지정", + "1": "활성", + "2": "비활성" + }, + "ACTIVATED": "제공자가 활성화되었습니다.", + "DEACTIVATED": "제공자가 비활성화되었습니다.", + "TWILIO": { + "SID": "SID", + "TOKEN": "토큰", + "SENDERNUMBER": "발신 번호", + "VERIFYSERVICESID": "검증 서비스 SID", + "VERIFYSERVICESID_DESCRIPTION": "검증 서비스 SID를 설정하면 전화번호 검증 및 OTP SMS에 Twilio 검증 서비스를 사용할 수 있습니다.", + "ADDED": "Twilio가 성공적으로 추가되었습니다.", + "UPDATED": "Twilio가 성공적으로 업데이트되었습니다.", + "REMOVED": "Twilio가 제거되었습니다.", + "CHANGETOKEN": "토큰 변경", + "SETTOKEN": "토큰 설정", + "TOKENSET": "토큰이 성공적으로 설정되었습니다." + } + }, + "SECRETS": { + "TYPES": "시크릿 유형", + "TYPE": { + "1": "초기화 메일", + "2": "이메일 검증", + "3": "전화 검증", + "4": "비밀번호 재설정", + "5": "비밀번호 없는 초기화", + "6": "앱 시크릿", + "7": "일회성 비밀번호 (OTP) - SMS", + "8": "일회성 비밀번호 (OTP) - 이메일" + }, + "EXPIRY": "만료 시간 (분)", + "INCLUDEDIGITS": "숫자 포함", + "INCLUDESYMBOLS": "기호 포함", + "INCLUDELOWERLETTERS": "소문자 포함", + "INCLUDEUPPERLETTERS": "대문자 포함", + "LENGTH": "길이", + "UPDATED": "설정이 업데이트되었습니다." + }, + "SECURITY": { + "IFRAMETITLE": "iFrame", + "IFRAMEDESCRIPTION": "이 설정은 CSP를 통해 허용된 도메인에서 프레이밍을 허용합니다. iFrame 사용을 허용하면 클릭재킹 위험이 있습니다.", + "IFRAMEENABLED": "iFrame 허용", + "ALLOWEDORIGINS": "허용된 URL", + "IMPERSONATIONTITLE": "신원 가장", + "IMPERSONATIONENABLED": "신원 가장 허용", + "IMPERSONATIONDESCRIPTION": "이 설정은 기본적으로 신원 가장을 사용할 수 있도록 합니다. 신원 가장하는 계정에는 적절한 `*_IMPERSONATOR` 역할이 할당되어야 합니다." + }, + "FEATURES": { + "LOGINDEFAULTORG": "로그인 기본 조직", + "LOGINDEFAULTORG_DESCRIPTION": "조직 컨텍스트가 설정되지 않은 경우 로그인 UI가 기본 조직의 설정을 사용합니다 (인스턴스에서 설정되지 않음).", + "OIDCLEGACYINTROSPECTION": "OIDC 레거시 내부 조사", + "OIDCLEGACYINTROSPECTION_DESCRIPTION": "최근 내부 조사 엔드포인트를 성능을 위해 리팩토링했습니다. 예상치 못한 버그가 발생하면 이 기능을 사용하여 레거시 구현으로 롤백할 수 있습니다.", + "OIDCTOKENEXCHANGE": "OIDC 토큰 교환", + "OIDCTOKENEXCHANGE_DESCRIPTION": "OIDC 토큰 엔드포인트의 실험적 urn:ietf:params:oauth:grant-type:token-exchange 허용을 활성화합니다. 토큰 교환을 통해 범위가 좁은 토큰을 요청하거나 다른 사용자를 가장할 수 있습니다. 인스턴스에서 가장을 허용하는 보안 정책을 확인하세요.", + "OIDCTRIGGERINTROSPECTIONPROJECTIONS": "OIDC 트리거 내부 조사 프로젝션", + "OIDCTRIGGERINTROSPECTIONPROJECTIONS_DESCRIPTION": "내부 조사 요청 중 프로젝션 트리거를 활성화합니다. 이는 내부 조사 응답에서 일관성 문제가 있는 경우 임시 해결책으로 작동할 수 있으나 성능에 영향을 미칠 수 있습니다. 향후 내부 조사 요청에 대한 트리거 제거를 계획 중입니다.", + "USERSCHEMA": "사용자 스키마", + "USERSCHEMA_DESCRIPTION": "사용자 스키마를 통해 사용자의 데이터 스키마를 관리할 수 있습니다. 플래그가 활성화되면 새 API 및 기능을 사용할 수 있습니다.", + "ACTIONS": "액션", + "ACTIONS_DESCRIPTION": "액션 v2는 데이터 실행 및 대상을 관리할 수 있습니다. 플래그가 활성화되면 새 API 및 기능을 사용할 수 있습니다.", + "OIDCSINGLEV1SESSIONTERMINATION": "OIDC 단일 V1 세션 종료", + "OIDCSINGLEV1SESSIONTERMINATION_DESCRIPTION": "플래그가 활성화되면, `sid` 클레임이 있는 id_token을 사용하여 end_session 엔드포인트에서 로그인 UI의 단일 세션을 종료할 수 있습니다. 현재 동일한 사용자 에이전트(브라우저)에서 모든 세션이 로그인 UI에서 종료됩니다. Session API를 통해 관리된 세션은 이미 단일 세션 종료를 허용합니다.", + "STATES": { + "INHERITED": "상속", + "ENABLED": "활성화됨", + "DISABLED": "비활성화됨" + }, + "INHERITED_DESCRIPTION": "시스템의 기본값으로 값을 설정합니다.", + "INHERITEDINDICATOR_DESCRIPTION": { + "ENABLED": "\"활성화됨\"은 상속되었습니다.", + "DISABLED": "\"비활성화됨\"은 상속되었습니다." + }, + "RESET": "모두 상속으로 설정" + }, + "DIALOG": { + "RESET": { + "DEFAULTTITLE": "설정 재설정", + "DEFAULTDESCRIPTION": "설정을 인스턴스의 기본 구성으로 재설정하려고 합니다. 계속하시겠습니까?", + "LOGINPOLICY_DESCRIPTION": "경고: 계속하면 ID 제공자 설정도 인스턴스 설정으로 재설정됩니다." + } + } + }, + "POLICY": { + "APPLIEDTO": "적용 대상", + "PWD_COMPLEXITY": { + "TITLE": "비밀번호 복잡성", + "DESCRIPTION": "모든 설정된 비밀번호가 특정 패턴에 맞는지 확인합니다", + "SYMBOLANDNUMBERERROR": "숫자와 기호/구두점이 포함되어야 합니다.", + "SYMBOLERROR": "기호/구두점이 포함되어야 합니다.", + "NUMBERERROR": "숫자가 포함되어야 합니다.", + "PATTERNERROR": "비밀번호가 요구되는 패턴에 맞지 않습니다." + }, + "NOTIFICATION": { + "TITLE": "알림", + "DESCRIPTION": "어떤 변경 사항에 대해 알림을 보낼지 결정합니다.", + "PASSWORDCHANGE": "비밀번호 변경" + }, + "PRIVATELABELING": { + "DESCRIPTION": "로그인을 맞춤형 스타일로 제공하고 동작을 수정하세요.", + "PREVIEW_DESCRIPTION": "정책의 변경 사항이 미리보기 환경에 자동으로 배포됩니다.", + "BTN": "파일 선택", + "ACTIVATEPREVIEW": "구성 적용", + "DARK": "다크 모드", + "LIGHT": "라이트 모드", + "CHANGEVIEW": "보기 변경", + "ACTIVATED": "정책 변경이 이제 실시간으로 반영됩니다", + "THEME": "테마", + "COLORS": "색상", + "FONT": "폰트", + "ADVANCEDBEHAVIOR": "고급 동작", + "DROP": "이미지를 여기로 드롭하거나", + "RELEASE": "릴리스", + "DROPFONT": "폰트 파일을 여기로 드롭하세요", + "RELEASEFONT": "릴리스", + "USEOFLOGO": "로그인이나 이메일에 로고가 사용됩니다. 작은 UI 요소에는 아이콘이 사용됩니다.", + "MAXSIZE": "최대 크기는 524kB로 제한됩니다", + "EMAILNOSVG": "이메일에서는 SVG 파일 형식이 지원되지 않습니다. PNG 또는 지원되는 다른 형식의 로고를 업로드하세요.", + "MAXSIZEEXCEEDED": "최대 크기인 524kB를 초과했습니다.", + "NOSVGSUPPORTED": "SVG는 지원되지 않습니다!", + "FONTINLOGINONLY": "현재 폰트는 로그인 인터페이스에서만 표시됩니다.", + "BACKGROUNDCOLOR": "배경 색상", + "PRIMARYCOLOR": "기본 색상", + "WARNCOLOR": "경고 색상", + "FONTCOLOR": "폰트 색상", + "VIEWS": { + "PREVIEW": "미리보기", + "CURRENT": "현재 구성" + }, + "PREVIEW": { + "TITLE": "로그인", + "SECOND": "ZITADEL 계정으로 로그인하세요.", + "ERROR": "사용자를 찾을 수 없습니다!", + "PRIMARYBUTTON": "다음", + "SECONDARYBUTTON": "등록" + }, + "THEMEMODE": { + "THEME_MODE_AUTO": "자동 모드", + "THEME_MODE_LIGHT": "라이트 모드만", + "THEME_MODE_DARK": "다크 모드만" + } + }, + "PWD_AGE": { + "TITLE": "비밀번호 만료", + "DESCRIPTION": "비밀번호 만료 정책을 설정할 수 있습니다. 이 정책은 만료 후 다음 로그인 시 비밀번호 변경을 강제합니다. 자동 경고 및 알림은 없습니다." + }, + "PWD_LOCKOUT": { + "TITLE": "잠금 정책", + "DESCRIPTION": "계정이 차단되기 전 최대 비밀번호 시도 횟수를 설정합니다." + }, + "PRIVATELABELING_POLICY": { + "TITLE": "브랜딩", + "BTN": "파일 선택", + "DESCRIPTION": "로그인의 외관을 맞춤화하세요", + "ACTIVATEPREVIEW": "구성 활성화" + }, + "LOGIN_POLICY": { + "TITLE": "로그인 설정", + "DESCRIPTION": "사용자 인증 방식을 정의하고 ID 제공자를 구성합니다", + "DESCRIPTIONCREATEADMIN": "사용자는 아래에서 사용할 수 있는 ID 제공자 중에서 선택할 수 있습니다.", + "DESCRIPTIONCREATEMGMT": "사용자는 아래에서 사용할 수 있는 ID 제공자 중에서 선택할 수 있습니다. 참고: 시스템 설정 제공자와 조직만을 위한 제공자를 사용할 수 있습니다.", + "LIFETIME_INVALID": "양식에 잘못된 값이 포함되어 있습니다.", + "SAVED": "성공적으로 저장되었습니다!", + "PROVIDER_ADDED": "ID 제공자가 활성화되었습니다." + }, + "PRIVACY_POLICY": { + "DESCRIPTION": "개인정보처리방침 및 이용 약관 링크 설정", + "TOSLINK": "이용 약관 링크", + "POLICYLINK": "개인정보처리방침 링크", + "HELPLINK": "도움말 링크", + "SUPPORTEMAIL": "지원 이메일", + "DOCSLINK": "문서 링크 (콘솔)", + "CUSTOMLINK": "사용자 정의 링크 (콘솔)", + "CUSTOMLINKTEXT": "사용자 정의 링크 텍스트 (콘솔)", + "SAVED": "성공적으로 저장되었습니다!", + "RESET_TITLE": "기본값 복원", + "RESET_DESCRIPTION": "이용 약관 및 개인정보처리방침의 기본 링크를 복원하려고 합니다. 계속하시겠습니까?" + }, + "LOGIN_TEXTS": { + "TITLE": "로그인 인터페이스 텍스트", + "DESCRIPTION": "로그인 인터페이스의 텍스트를 정의하세요. 텍스트가 비어 있으면 기본값이 플레이스홀더로 표시됩니다.", + "DESCRIPTION_SHORT": "로그인 인터페이스의 텍스트를 정의하세요.", + "NEWERVERSIONEXISTS": "새 버전이 존재합니다", + "CURRENTDATE": "현재 구성", + "CHANGEDATE": "새 버전 업데이트", + "KEYNAME": "로그인 화면 / 인터페이스", + "RESET_TITLE": "기본값 복원", + "RESET_DESCRIPTION": "모든 기본값을 복원하려고 합니다. 모든 변경 사항이 영구적으로 삭제됩니다. 계속하시겠습니까?", + "UNSAVED_TITLE": "저장하지 않고 계속하시겠습니까?", + "UNSAVED_DESCRIPTION": "저장하지 않고 변경했습니다. 지금 저장하시겠습니까?", + "ACTIVE_LANGUAGE_NOT_ALLOWED": "허용되지 않은 언어를 선택했습니다. 텍스트를 수정할 수는 있지만, 사용자가 이 언어를 실제로 사용할 수 있도록 하려면 인스턴스 제한을 변경하세요.", + "LANGUAGES_NOT_ALLOWED": "허용되지 않은 언어:", + "LANGUAGE": "언어", + "LANGUAGES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски", + "cs": "Čeština", + "ru": "Русский", + "nl": "Nederlands", + "sv": "Svenska", + "id": "Bahasa Indonesia", + "ko": "한국어" + }, + "KEYS": { + "emailVerificationDoneText": "이메일 인증 완료", + "emailVerificationText": "이메일 인증", + "externalUserNotFoundText": "외부 사용자를 찾을 수 없습니다", + "footerText": "푸터", + "initMfaDoneText": "MFA 초기화 완료", + "initMfaOtpText": "MFA 초기화", + "initMfaPromptText": "MFA 초기화 프롬프트", + "initMfaU2fText": "U2F 초기화", + "initPasswordDoneText": "비밀번호 초기화 완료", + "initPasswordText": "비밀번호 초기화", + "initializeDoneText": "사용자 초기화 완료", + "initializeUserText": "사용자 초기화", + "linkingUserDoneText": "사용자 연결 완료", + "loginText": "로그인", + "logoutText": "로그아웃", + "mfaProvidersText": "MFA 제공자", + "passwordChangeDoneText": "비밀번호 변경 완료", + "passwordChangeText": "비밀번호 변경", + "passwordResetDoneText": "비밀번호 재설정 완료", + "passwordText": "비밀번호", + "registrationOptionText": "등록 옵션", + "registrationOrgText": "조직 등록", + "registrationUserText": "사용자 등록", + "selectAccountText": "계정 선택", + "successLoginText": "성공적으로 로그인", + "usernameChangeDoneText": "사용자 이름 변경 완료", + "usernameChangeText": "사용자 이름 변경", + "verifyMfaOtpText": "OTP 확인", + "verifyMfaU2fText": "U2F 확인", + "passwordlessPromptText": "비밀번호 없는 프롬프트", + "passwordlessRegistrationDoneText": "비밀번호 없는 등록 완료", + "passwordlessRegistrationText": "비밀번호 없는 등록", + "passwordlessText": "비밀번호 없음", + "externalRegistrationUserOverviewText": "외부 등록 사용자 개요" + } + }, + "MESSAGE_TEXTS": { + "TYPE": "알림", + "TYPES": { + "INIT": "초기화", + "VE": "이메일 확인", + "VP": "전화 확인", + "VSO": "SMS OTP 확인", + "VEO": "이메일 OTP 확인", + "PR": "비밀번호 재설정", + "DC": "도메인 클레임", + "PL": "비밀번호 없음", + "PC": "비밀번호 변경", + "IU": "사용자 초대" + }, + "CHIPS": { + "firstname": "이름", + "lastname": "성", + "code": "코드", + "preferredLoginName": "선호 로그인 이름", + "displayName": "표시 이름", + "nickName": "닉네임", + "loginnames": "로그인 이름", + "domain": "도메인", + "lastEmail": "마지막 이메일", + "lastPhone": "마지막 전화번호", + "verifiedEmail": "확인된 이메일", + "verifiedPhone": "확인된 전화번호", + "changedate": "변경 날짜", + "username": "사용자 이름", + "tempUsername": "임시 사용자 이름", + "otp": "일회용 비밀번호", + "verifyUrl": "일회용 비밀번호 확인 URL", + "expiry": "만료", + "applicationName": "애플리케이션 이름" + }, + "TOAST": { + "UPDATED": "사용자 정의 텍스트가 저장되었습니다." + } + }, + "DEFAULTLABEL": "현재 설정이 인스턴스의 표준과 일치합니다.", + "BTN_INSTALL": "설치", + "BTN_EDIT": "수정", + "DATA": { + "DESCRIPTION": "설명", + "MINLENGTH": "최소 길이여야 함", + "HASNUMBER": "숫자를 포함해야 함", + "HASSYMBOL": "기호를 포함해야 함", + "HASLOWERCASE": "소문자를 포함해야 함", + "HASUPPERCASE": "대문자를 포함해야 함", + "SHOWLOCKOUTFAILURES": "잠금 실패 표시", + "MAXPASSWORDATTEMPTS": "비밀번호 최대 시도 횟수", + "MAXOTPATTEMPTS": "OTP 최대 시도 횟수", + "EXPIREWARNDAYS": "만료 경고 (일)", + "MAXAGEDAYS": "최대 유효 기간 (일)", + "USERLOGINMUSTBEDOMAIN": "로그인 이름에 조직 도메인 추가", + "USERLOGINMUSTBEDOMAIN_DESCRIPTION": "이 설정을 활성화하면 모든 로그인 이름에 조직 도메인이 추가됩니다. 이 설정이 비활성화된 경우, 모든 조직에서 사용자 이름이 고유하도록 해야 합니다.", + "VALIDATEORGDOMAINS": "조직 도메인 확인 필요 (DNS 또는 HTTP 검증)", + "SMTPSENDERADDRESSMATCHESINSTANCEDOMAIN": "SMTP 발신 주소가 인스턴스 도메인과 일치해야 함", + "ALLOWUSERNAMEPASSWORD_DESC": "사용자 이름과 비밀번호로의 일반적인 로그인이 허용됩니다.", + "ALLOWEXTERNALIDP_DESC": "기본 ID 제공자에 대한 로그인이 허용됩니다.", + "ALLOWREGISTER_DESC": "옵션이 선택되면, 로그인 중 사용자 등록을 위한 추가 단계가 표시됩니다.", + "FORCEMFA": "MFA 강제 적용", + "FORCEMFALOCALONLY": "로컬 인증 사용자에 대해 MFA 강제 적용", + "FORCEMFALOCALONLY_DESC": "옵션이 선택되면, 로컬 인증 사용자는 로그인 시 두 번째 인증 요소를 구성해야 합니다.", + "HIDEPASSWORDRESET_DESC": "옵션이 선택되면, 사용자가 로그인 과정에서 비밀번호를 재설정할 수 없습니다.", + "HIDELOGINNAMESUFFIX": "로그인 이름 접미사 숨기기", + "HIDELOGINNAMESUFFIX_DESC": "로그인 인터페이스에서 로그인 이름 접미사를 숨깁니다.", + "IGNOREUNKNOWNUSERNAMES_DESC": "옵션이 선택되면, 사용자를 찾을 수 없는 경우에도 로그인 과정에서 비밀번호 화면이 표시됩니다. 비밀번호 확인 오류가 사용자 이름 또는 비밀번호의 오류를 공개하지 않습니다.", + "ALLOWDOMAINDISCOVERY_DESC": "옵션이 선택되면, 로그인 화면에서 알 수 없는 사용자 이름의 접미사 (@domain.com)가 조직 도메인과 일치하고, 일치할 경우 해당 조직의 등록 화면으로 리디렉션됩니다.", + "DEFAULTREDIRECTURI": "기본 리디렉션 URI", + "DEFAULTREDIRECTURI_DESC": "앱 컨텍스트 없이 로그인 시작 시 (예: 이메일에서) 사용자가 리디렉션될 위치를 정의합니다.", + "ERRORMSGPOPUP": "팝업에 오류 표시", + "DISABLEWATERMARK": "워터마크 숨기기", + "DISABLEWATERMARK_DESC": "로그인 인터페이스에서 'Powered by ZITADEL' 워터마크를 숨깁니다." + }, + "RESET": "인스턴스 기본값으로 재설정", + "CREATECUSTOM": "사용자 정의 정책 생성", + "TOAST": { + "SET": "정책이 성공적으로 설정되었습니다!", + "RESETSUCCESS": "정책이 성공적으로 재설정되었습니다!", + "UPLOADSUCCESS": "업로드가 성공적으로 완료되었습니다!", + "DELETESUCCESS": "성공적으로 삭제되었습니다!", + "UPLOADFAILED": "업로드 실패!" + } + }, + "ORG_DETAIL": { + "TITLE": "조직", + "DESCRIPTION": "여기에서 조직의 설정을 편집하고 멤버를 관리할 수 있습니다.", + "DETAIL": { + "TITLE": "세부 정보", + "NAME": "이름", + "DOMAIN": "도메인", + "STATE": { + "0": "정의되지 않음", + "1": "활성화", + "2": "비활성화" + } + }, + "MEMBER": { + "TITLE": "멤버", + "USERNAME": "사용자 이름", + "DISPLAYNAME": "표시 이름", + "LOGINNAME": "로그인 이름", + "EMAIL": "이메일", + "ROLES": "역할", + "ADD": "멤버 추가", + "ADDDESCRIPTION": "추가할 사용자 이름을 입력하세요." + }, + "TABLE": { + "TOTAL": "총 항목", + "SELECTION": "선택한 요소", + "DEACTIVATE": "사용자 비활성화", + "ACTIVATE": "사용자 활성화", + "DELETE": "사용자 삭제", + "CLEAR": "선택 지우기" + } + }, + "PROJECT": { + "PAGES": { + "TITLE": "프로젝트", + "DESCRIPTION": "여기에서 애플리케이션을 정의하고, 역할을 관리하며 다른 조직이 프로젝트를 사용할 수 있도록 권한을 부여할 수 있습니다.", + "DELETE": "프로젝트 삭제", + "DETAIL": "세부 정보", + "CREATE": "프로젝트 생성", + "CREATE_DESC": "프로젝트의 이름을 입력하세요.", + "ROLE": "역할", + "NOITEMS": "프로젝트가 없습니다", + "ZITADELPROJECT": "이 프로젝트는 ZITADEL 프로젝트에 속해 있습니다. 변경 시 ZITADEL이 의도한 대로 작동하지 않을 수 있습니다.", + "TYPE": { + "OWNED": "소유한 프로젝트", + "OWNED_SINGULAR": "소유한 프로젝트", + "GRANTED_SINGULAR": "{{name}}의 허가된 프로젝트" + }, + "PRIVATELABEL": { + "TITLE": "브랜딩 설정", + "0": { + "TITLE": "정의되지 않음", + "DESC": "사용자가 식별되면 식별된 사용자의 조직 브랜딩이 표시되며, 그렇지 않으면 시스템 기본값이 표시됩니다." + }, + "1": { + "TITLE": "프로젝트 설정 사용", + "DESC": "프로젝트를 소유한 조직의 브랜딩이 표시됩니다" + }, + "2": { + "TITLE": "사용자 조직 설정 사용", + "DESC": "프로젝트의 조직 브랜딩이 표시되지만 사용자가 식별되면 식별된 사용자의 조직 설정이 표시됩니다." + }, + "DIALOG": { + "TITLE": "브랜딩 설정", + "DESCRIPTION": "프로젝트 사용 시 로그인 동작을 선택하세요." + } + }, + "PINNED": "고정됨", + "ALL": "모두", + "CREATEDON": "생성일", + "LASTMODIFIED": "마지막 수정일", + "ADDNEW": "새 프로젝트 생성", + "DIALOG": { + "REACTIVATE": { + "TITLE": "프로젝트 재활성화", + "DESCRIPTION": "프로젝트를 정말로 재활성화하시겠습니까?" + }, + "DEACTIVATE": { + "TITLE": "프로젝트 비활성화", + "DESCRIPTION": "프로젝트를 정말로 비활성화하시겠습니까?" + }, + "DELETE": { + "TITLE": "프로젝트 삭제", + "DESCRIPTION": "프로젝트를 정말로 삭제하시겠습니까?", + "TYPENAME": "영구적으로 삭제하려면 프로젝트 이름을 입력하세요." + } + } + }, + "SETTINGS": { + "TITLE": "설정", + "DESCRIPTION": "" + }, + "STATE": { + "TITLE": "상태", + "0": "정의되지 않음", + "1": "활성화", + "2": "비활성화" + }, + "TYPE": { + "TITLE": "유형", + "0": "알 수 없는 유형", + "1": "소유한 프로젝트", + "2": "허가된 프로젝트" + }, + "NAME": "이름", + "NAMEDIALOG": { + "TITLE": "프로젝트 이름 변경", + "DESCRIPTION": "프로젝트의 새 이름을 입력하세요", + "NAME": "새 이름" + }, + "MEMBER": { + "TITLE": "관리자", + "TITLEDESC": "관리자는 역할에 따라 이 프로젝트를 변경할 수 있습니다.", + "DESCRIPTION": "이 관리자는 프로젝트를 편집할 수 있습니다.", + "USERNAME": "사용자 이름", + "DISPLAYNAME": "표시 이름", + "LOGINNAME": "로그인 이름", + "EMAIL": "이메일", + "ROLES": "역할", + "USERID": "사용자 ID" + }, + "GRANT": { + "EMPTY": "허가된 조직이 없습니다.", + "TITLE": "프로젝트 권한", + "DESCRIPTION": "다른 조직이 프로젝트를 사용할 수 있도록 허용합니다.", + "EDITTITLE": "역할 수정", + "CREATE": { + "TITLE": "조직 권한 생성", + "SEL_USERS": "액세스를 허가할 사용자를 선택하세요", + "SEL_PROJECT": "프로젝트를 검색하세요", + "SEL_ROLES": "권한에 추가할 역할을 선택하세요", + "SEL_USER": "사용자 선택", + "SEL_ORG": "조직 검색", + "SEL_ORG_DESC": "권한을 부여할 조직을 검색하세요.", + "ORG_DESCRIPTION": "{{name}} 조직의 사용자에게 권한을 부여하려고 합니다.", + "ORG_DESCRIPTION_DESC": "헤더의 컨텍스트를 전환하여 다른 조직에 대한 사용자 권한을 부여할 수 있습니다.", + "SEL_ORG_FORMFIELD": "조직", + "FOR_ORG": "권한이 생성된 대상:" + }, + "DETAIL": { + "TITLE": "프로젝트 권한", + "DESC": "특정 조직이 사용할 수 있는 역할을 선택하고 관리자를 지정할 수 있습니다.", + "MEMBERTITLE": "관리자", + "MEMBERDESC": "권한이 부여된 조직의 관리자입니다. 프로젝트 데이터 편집 권한을 부여할 사용자를 추가하세요.", + "PROJECTNAME": "프로젝트 이름", + "GRANTEDORG": "권한 부여된 조직", + "RESOURCEOWNER": "리소스 소유자" + }, + "STATE": "상태", + "STATES": { + "1": "활성화", + "2": "비활성화" + }, + "ALL": "모두", + "SHOWDETAIL": "세부 정보 보기", + "USER": "사용자", + "MEMBERS": "관리자", + "ORG": "조직", + "PROJECTNAME": "프로젝트 이름", + "GRANTEDORG": "권한 부여된 조직", + "GRANTEDORGDOMAIN": "도메인", + "RESOURCEOWNER": "리소스 소유자", + "GRANTEDORGNAME": "조직 이름", + "GRANTID": "권한 ID", + "CREATIONDATE": "생성일", + "CHANGEDATE": "마지막 수정일", + "DATES": "날짜", + "ROLENAMESLIST": "역할", + "NOROLES": "역할 없음", + "TYPE": "유형", + "TOAST": { + "PROJECTGRANTUSERGRANTADDED": "프로젝트 권한이 생성되었습니다.", + "PROJECTGRANTADDED": "프로젝트 권한이 생성되었습니다.", + "PROJECTGRANTCHANGED": "프로젝트 권한이 변경되었습니다.", + "PROJECTGRANTMEMBERADDED": "권한 관리자가 추가되었습니다.", + "PROJECTGRANTMEMBERCHANGED": "권한 관리자가 변경되었습니다.", + "PROJECTGRANTMEMBERREMOVED": "권한 관리자가 제거되었습니다.", + "PROJECTGRANTUPDATED": "프로젝트 권한이 업데이트되었습니다." + }, + "DIALOG": { + "DELETE_TITLE": "프로젝트 권한 삭제", + "DELETE_DESCRIPTION": "프로젝트 권한을 삭제하려고 합니다. 정말 삭제하시겠습니까?" + }, + "ROLES": "프로젝트 역할" + }, + "APP": { + "TITLE": "애플리케이션", + "NAME": "이름", + "NAMEREQUIRED": "이름이 필요합니다." + }, + "ROLE": { + "EMPTY": "아직 생성된 역할이 없습니다.", + "ADDNEWLINE": "추가 역할 추가", + "KEY": "키", + "TITLE": "역할", + "DESCRIPTION": "프로젝트 권한을 생성하는 데 사용할 수 있는 역할을 정의하세요.", + "NAME": "이름", + "DISPLAY_NAME": "표시 이름", + "GROUP": "그룹", + "ACTIONS": "작업", + "ADDTITLE": "역할 생성", + "ADDDESCRIPTION": "새 역할에 대한 데이터를 입력하세요.", + "EDITTITLE": "역할 수정", + "EDITDESCRIPTION": "역할의 새 데이터를 입력하세요.", + "DELETE": "역할 삭제", + "CREATIONDATE": "생성일", + "CHANGEDATE": "마지막 수정일", + "SELECTGROUPTOOLTIP": "{{group}} 그룹의 모든 역할을 선택하세요.", + "OPTIONS": "옵션", + "ASSERTION": "인증 시 역할 검증", + "ASSERTION_DESCRIPTION": "역할 정보는 Userinfo 엔드포인트에서 전송되며, 애플리케이션 설정에 따라 토큰 및 기타 형식으로 전송될 수 있습니다.", + "CHECK": "인증 시 권한 확인", + "CHECK_DESCRIPTION": "설정 시, 계정에 역할이 할당된 사용자만 인증할 수 있습니다.", + "DIALOG": { + "DELETE_TITLE": "역할 삭제", + "DELETE_DESCRIPTION": "프로젝트 역할을 삭제하려고 합니다. 정말 삭제하시겠습니까?" + } + }, + "HAS_PROJECT": "인증 시 프로젝트 확인", + "HAS_PROJECT_DESCRIPTION": "사용자의 조직에 이 프로젝트가 있는지 확인합니다. 없으면 인증할 수 없습니다.", + "TABLE": { + "TOTAL": "총 항목 수:", + "SELECTION": "선택한 요소", + "DEACTIVATE": "프로젝트 비활성화", + "ACTIVATE": "프로젝트 활성화", + "DELETE": "프로젝트 삭제", + "ORGNAME": "조직 이름", + "ORGDOMAIN": "조직 도메인", + "STATE": "상태", + "TYPE": "유형", + "CREATIONDATE": "생성일", + "CHANGEDATE": "마지막 수정일", + "RESOURCEOWNER": "소유자", + "SHOWTABLE": "표 보기", + "SHOWGRID": "그리드 보기", + "EMPTY": "찾은 프로젝트가 없습니다" + }, + "TOAST": { + "MEMBERREMOVED": "관리자가 제거되었습니다.", + "MEMBERSADDED": "관리자가 추가되었습니다.", + "MEMBERADDED": "관리자가 추가되었습니다.", + "MEMBERCHANGED": "관리자가 변경되었습니다.", + "ROLESCREATED": "역할이 생성되었습니다.", + "ROLEREMOVED": "역할이 제거되었습니다.", + "ROLECHANGED": "역할이 변경되었습니다.", + "REACTIVATED": "재활성화되었습니다.", + "DEACTIVATED": "비활성화되었습니다.", + "CREATED": "프로젝트가 생성되었습니다.", + "UPDATED": "프로젝트가 변경되었습니다.", + "GRANTUPDATED": "권한이 변경되었습니다.", + "DELETED": "프로젝트가 삭제되었습니다." + } + }, + "ROLES": { + "DIALOG": { + "DELETE_TITLE": "역할 삭제", + "DELETE_DESCRIPTION": "역할을 삭제하려고 합니다. 정말로 삭제하시겠습니까?" + } + }, + "NEXTSTEPS": { + "TITLE": "다음 단계" + }, + "IDP": { + "LIST": { + "ACTIVETITLE": "활성화된 ID 제공자" + }, + "CREATE": { + "TITLE": "제공자 추가", + "DESCRIPTION": "다음 제공자 중 하나 이상을 선택하세요.", + "STEPPERTITLE": "제공자 생성", + "OIDC": { + "TITLE": "OIDC 제공자", + "DESCRIPTION": "OIDC 제공자에 필요한 데이터를 입력하세요." + }, + "OAUTH": { + "TITLE": "OAuth 제공자", + "DESCRIPTION": "OAuth 제공자에 필요한 데이터를 입력하세요." + }, + "JWT": { + "TITLE": "JWT 제공자", + "DESCRIPTION": "JWT 제공자에 필요한 데이터를 입력하세요." + }, + "GOOGLE": { + "TITLE": "Google 제공자", + "DESCRIPTION": "Google ID 제공자의 자격 증명을 입력하세요." + }, + "GITLAB": { + "TITLE": "Gitlab 제공자", + "DESCRIPTION": "Gitlab ID 제공자의 자격 증명을 입력하세요." + }, + "GITLABSELFHOSTED": { + "TITLE": "자체 호스팅된 Gitlab 제공자", + "DESCRIPTION": "자체 호스팅된 Gitlab ID 제공자의 자격 증명을 입력하세요." + }, + "GITHUBES": { + "TITLE": "GitHub 엔터프라이즈 서버 제공자", + "DESCRIPTION": "GitHub 엔터프라이즈 서버 ID 제공자의 자격 증명을 입력하세요." + }, + "GITHUB": { + "TITLE": "Github 제공자", + "DESCRIPTION": "Github ID 제공자의 자격 증명을 입력하세요." + }, + "AZUREAD": { + "TITLE": "Microsoft 제공자", + "DESCRIPTION": "Microsoft ID 제공자의 자격 증명을 입력하세요." + }, + "LDAP": { + "TITLE": "Active Directory / LDAP", + "DESCRIPTION": "LDAP 제공자의 자격 증명을 입력하세요." + }, + "APPLE": { + "TITLE": "Apple로 로그인", + "DESCRIPTION": "Apple 제공자의 자격 증명을 입력하세요." + }, + "SAML": { + "TITLE": "SAML로 로그인", + "DESCRIPTION": "SAML 제공자의 자격 증명을 입력하세요." + } + }, + "DETAIL": { + "TITLE": "ID 제공자", + "DESCRIPTION": "제공자 설정을 업데이트하세요.", + "DATECREATED": "생성됨", + "DATECHANGED": "변경됨" + }, + "OPTIONS": { + "ISAUTOCREATION": "자동 생성", + "ISAUTOCREATION_DESC": "선택 시, 계정이 존재하지 않으면 생성됩니다.", + "ISAUTOUPDATE": "자동 업데이트", + "ISAUTOUPDATE_DESC": "선택 시, 재인증 시 계정이 업데이트됩니다.", + "ISCREATIONALLOWED": "계정 생성 허용 (수동)", + "ISCREATIONALLOWED_DESC": "외부 계정을 사용하여 계정을 생성할 수 있는지 결정합니다. 자동 생성이 활성화된 경우 사용자가 계정 정보를 수정하지 못하도록 설정할 수 있습니다.", + "ISLINKINGALLOWED": "계정 연결 허용 (수동)", + "ISLINKINGALLOWED_DESC": "ID를 기존 계정에 수동으로 연결할 수 있는지 결정합니다. 자동 연결이 활성화된 경우 사용자가 제안된 계정만 연결하도록 설정할 수 있습니다.", + "AUTOLINKING_DESC": "ID가 기존 계정에 연결되도록 요청할지 여부를 결정합니다.", + "AUTOLINKINGTYPE": { + "0": "비활성화됨", + "1": "기존 사용자 이름 확인", + "2": "기존 이메일 확인" + } + }, + "OWNERTYPES": { + "0": "알 수 없음", + "1": "인스턴스", + "2": "조직" + }, + "STATES": { + "1": "활성화", + "2": "비활성화" + }, + "AZUREADTENANTTYPES": { + "3": "테넌트 ID", + "0": "공통", + "1": "조직", + "2": "소비자" + }, + "AZUREADTENANTTYPE": "테넌트 유형", + "AZUREADTENANTID": "테넌트 ID", + "EMAILVERIFIED": "이메일 인증됨", + "NAMEHINT": "지정하면 로그인 인터페이스에 표시됩니다.", + "OPTIONAL": "선택 사항", + "LDAPATTRIBUTES": "LDAP 속성", + "UPDATEBINDPASSWORD": "바인드 비밀번호 업데이트", + "UPDATECLIENTSECRET": "클라이언트 시크릿 업데이트", + "ADD": "ID 제공자 추가", + "TYPE": "유형", + "OWNER": "소유자", + "ID": "ID", + "NAME": "이름", + "AUTHORIZATIONENDPOINT": "인증 엔드포인트", + "TOKENENDPOINT": "토큰 엔드포인트", + "USERENDPOINT": "사용자 엔드포인트", + "IDATTRIBUTE": "ID 속성", + "AVAILABILITY": "가용성", + "AVAILABLE": "사용 가능", + "AVAILABLEBUTINACTIVE": "사용 가능하지만 비활성화됨", + "SETAVAILABLE": "사용 가능으로 설정", + "SETUNAVAILABLE": "사용 불가로 설정", + "CONFIG": "구성", + "STATE": "상태", + "ISSUER": "발급자", + "SCOPESLIST": "스코프 목록", + "CLIENTID": "클라이언트 ID", + "CLIENTSECRET": "클라이언트 시크릿", + "LDAPCONNECTION": "연결", + "LDAPUSERBINDING": "사용자 바인딩", + "BASEDN": "기준 DN", + "BINDDN": "바인드 DN", + "BINDPASSWORD": "바인드 비밀번호", + "SERVERS": "서버", + "STARTTLS": "TLS 시작", + "TIMEOUT": "타임아웃 (초)", + "USERBASE": "사용자 베이스", + "USERFILTERS": "사용자 필터", + "USEROBJECTCLASSES": "사용자 객체 클래스", + "REQUIRED": "필수", + "LDAPIDATTRIBUTE": "ID 속성", + "AVATARURLATTRIBUTE": "아바타 URL 속성", + "DISPLAYNAMEATTRIBUTE": "표시 이름 속성", + "EMAILATTRIBUTEATTRIBUTE": "이메일 속성", + "EMAILVERIFIEDATTRIBUTE": "이메일 인증 속성", + "FIRSTNAMEATTRIBUTE": "이름 속성", + "LASTNAMEATTRIBUTE": "성 속성", + "NICKNAMEATTRIBUTE": "닉네임 속성", + "PHONEATTRIBUTE": "전화번호 속성", + "PHONEVERIFIEDATTRIBUTE": "전화 인증 속성", + "PREFERREDLANGUAGEATTRIBUTE": "선호 언어 속성", + "PREFERREDUSERNAMEATTRIBUTE": "선호 사용자 이름 속성", + "PROFILEATTRIBUTE": "프로필 속성", + "IDPDISPLAYNAMMAPPING": "ID 제공자 표시 이름 매핑", + "USERNAMEMAPPING": "사용자 이름 매핑", + "DATES": "날짜", + "CREATIONDATE": "생성 일자", + "CHANGEDATE": "마지막 수정 일자", + "DEACTIVATE": "비활성화", + "ACTIVATE": "활성화", + "DELETE": "삭제", + "DELETE_TITLE": "ID 제공자 삭제", + "DELETE_DESCRIPTION": "ID 제공자를 삭제하려고 합니다. 이 변경 사항은 되돌릴 수 없습니다. 정말로 삭제하시겠습니까?", + "REMOVE_WARN_TITLE": "ID 제공자 제거", + "REMOVE_WARN_DESCRIPTION": "ID 제공자를 제거하려고 합니다. 사용자가 선택할 수 있는 ID 제공자가 제거되며, 이미 등록된 사용자는 다시 로그인할 수 없습니다. 계속하시겠습니까?", + "DELETE_SELECTION_TITLE": "ID 제공자 삭제", + "DELETE_SELECTION_DESCRIPTION": "ID 제공자를 삭제하려고 합니다. 이 변경 사항은 되돌릴 수 없습니다. 정말로 삭제하시겠습니까?", + "EMPTY": "사용 가능한 ID 제공자가 없습니다.", + "OIDC": { + "GENERAL": "일반 정보", + "TITLE": "OIDC 구성", + "DESCRIPTION": "OIDC ID 제공자에 필요한 데이터를 입력하세요." + }, + "JWT": { + "TITLE": "JWT 구성", + "DESCRIPTION": "JWT ID 제공자에 필요한 데이터를 입력하세요.", + "HEADERNAME": "헤더 이름", + "JWTENDPOINT": "JWT 엔드포인트", + "JWTKEYSENDPOINT": "JWT 키 엔드포인트" + }, + "APPLE": { + "TEAMID": "팀 ID", + "KEYID": "키 ID", + "PRIVATEKEY": "개인 키", + "UPDATEPRIVATEKEY": "개인 키 업데이트", + "UPLOADPRIVATEKEY": "개인 키 업로드", + "KEYMAXSIZEEXCEEDED": "최대 크기 5kB 초과" + }, + "SAML": { + "METADATAXML": "메타데이터 XML", + "METADATAURL": "메타데이터 URL", + "BINDING": "바인딩", + "SIGNEDREQUEST": "서명된 요청", + "NAMEIDFORMAT": "NameID 형식", + "TRANSIENTMAPPINGATTRIBUTENAME": "사용자 매핑 속성 이름", + "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "`nameid-format`이 `transient`인 경우 사용자 매핑에 사용할 대체 속성 이름, 예: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`" + }, + "TOAST": { + "SAVED": "성공적으로 저장되었습니다.", + "REACTIVATED": "ID 제공자가 재활성화되었습니다.", + "DEACTIVATED": "ID 제공자가 비활성화되었습니다.", + "SELECTEDREACTIVATED": "선택된 ID 제공자가 재활성화되었습니다.", + "SELECTEDDEACTIVATED": "선택된 ID 제공자가 비활성화되었습니다.", + "SELECTEDKEYSDELETED": "선택된 ID 제공자가 삭제되었습니다.", + "DELETED": "ID 제공자가 성공적으로 제거되었습니다!", + "ADDED": "성공적으로 추가되었습니다.", + "REMOVED": "성공적으로 제거되었습니다." + }, + "ISIDTOKENMAPPING": "ID 토큰에서 매핑", + "ISIDTOKENMAPPING_DESC": "선택 시, 사용자 정보 엔드포인트가 아닌 ID 토큰에서 제공자 정보를 매핑합니다." + }, + "MFA": { + "LIST": { + "MULTIFACTORTITLE": "비밀번호 없는 인증", + "MULTIFACTORDESCRIPTION": "여기서 비밀번호 없는 인증을 위한 다중 인증 요소를 정의하세요.", + "SECONDFACTORTITLE": "다중 인증", + "SECONDFACTORDESCRIPTION": "비밀번호 인증을 강화할 추가 인증 요소를 정의하세요." + }, + "CREATE": { + "TITLE": "새로운 인증 요소", + "DESCRIPTION": "새로운 인증 요소 유형을 선택하세요." + }, + "DELETE": { + "TITLE": "인증 요소 삭제", + "DESCRIPTION": "로그인 설정에서 인증 요소를 삭제하려고 합니다. 정말로 삭제하시겠습니까?" + }, + "TOAST": { + "ADDED": "성공적으로 추가되었습니다.", + "SAVED": "성공적으로 저장되었습니다.", + "DELETED": "성공적으로 삭제되었습니다." + }, + "TYPE": "유형", + "MULTIFACTORTYPES": { + "0": "알 수 없음", + "1": "지문, 보안 키, Face ID 및 기타" + }, + "SECONDFACTORTYPES": { + "0": "알 수 없음", + "1": "인증 앱을 통한 일회성 비밀번호(TOTP)", + "2": "지문, 보안 키, Face ID 및 기타", + "3": "이메일을 통한 일회성 비밀번호(이메일 OTP)", + "4": "SMS를 통한 일회성 비밀번호(SMS OTP)" + } + }, + "LOGINPOLICY": { + "CREATE": { + "TITLE": "로그인 설정", + "DESCRIPTION": "조직 내 사용자의 인증 방법을 정의하세요." + }, + "IDPS": "아이덴티티 제공자", + "ADDIDP": { + "TITLE": "아이덴티티 제공자 추가", + "DESCRIPTION": "인증을 위해 사전 정의된 제공자 또는 사용자가 생성한 제공자를 선택할 수 있습니다.", + "SELECTIDPS": "아이덴티티 제공자" + }, + "PASSWORDLESS": "비밀번호 없는 로그인", + "PASSWORDLESSTYPE": { + "0": "허용되지 않음", + "1": "허용됨" + } + }, + "SMTP": { + "LIST": { + "TITLE": "SMTP 제공자", + "DESCRIPTION": "이것은 ZITADEL 인스턴스에 대한 SMTP 제공자 목록입니다. 사용자에게 알림을 전송할 제공자를 활성화하세요.", + "EMPTY": "사용 가능한 SMTP 제공자가 없습니다.", + "ACTIVATED": "활성화됨", + "ACTIVATE": "제공자 활성화", + "DEACTIVATE": "제공자 비활성화", + "TEST": "제공자 테스트", + "TYPE": "유형", + "DIALOG": { + "ACTIVATED": "SMTP 설정이 활성화되었습니다.", + "ACTIVATE_WARN_TITLE": "SMTP 설정 활성화", + "ACTIVATE_WARN_DESCRIPTION": "SMTP 설정을 활성화하려고 합니다. 현재 활성화된 제공자는 비활성화되며 새 설정이 활성화됩니다. 진행하시겠습니까?", + "DEACTIVATE_WARN_TITLE": "SMTP 설정 비활성화", + "DEACTIVATE_WARN_DESCRIPTION": "SMTP 설정을 비활성화하려고 합니다. 진행하시겠습니까?", + "DEACTIVATED": "SMTP 설정이 비활성화되었습니다.", + "DELETE_TITLE": "SMTP 설정 삭제", + "DELETE_DESCRIPTION": "구성을 삭제하려고 합니다. 발신자 이름을 입력하여 이 작업을 확인하세요.", + "DELETED": "SMTP 설정이 삭제되었습니다.", + "SENDER": "이 SMTP 설정을 삭제하려면 {{value}}을 입력하세요.", + "TEST_TITLE": "SMTP 설정 테스트", + "TEST_DESCRIPTION": "이 제공자의 SMTP 설정을 테스트할 이메일 주소를 지정하세요.", + "TEST_EMAIL": "이메일 주소", + "TEST_RESULT": "테스트 결과" + } + }, + "CREATE": { + "TITLE": "SMTP 제공자 추가", + "DESCRIPTION": "다음 제공자 중 하나 이상을 선택하세요.", + "STEPS": { + "TITLE": "{{ value }} SMTP 제공자 추가", + "CREATE_DESC_TITLE": "단계별로 {{ value }} SMTP 설정 입력", + "CURRENT_DESC_TITLE": "현재 SMTP 설정입니다.", + "PROVIDER_SETTINGS": "SMTP 제공자 설정", + "SENDER_SETTINGS": "발신자 설정", + "NEXT_STEPS": "다음 단계", + "ACTIVATE": { + "TITLE": "SMTP 제공자 활성화", + "DESCRIPTION": "SMTP 제공자를 활성화하지 않으면 ZITADEL이 알림을 전송할 수 없습니다. 이 제공자를 활성화하면 현재 활성화된 다른 제공자는 비활성화됩니다." + }, + "DEACTIVATE": { + "TITLE": "SMTP 제공자 비활성화", + "DESCRIPTION": "SMTP 제공자를 비활성화하면 다시 활성화할 때까지 ZITADEL이 이를 통해 알림을 전송할 수 없습니다." + }, + "SAVE_SETTINGS": "설정 저장", + "TEST": { + "TITLE": "설정 테스트", + "DESCRIPTION": "SMTP 제공자 설정을 테스트하고 저장하기 전에 테스트 결과를 확인하세요.", + "RESULT": "이메일이 성공적으로 전송되었습니다." + } + } + }, + "DETAIL": { + "TITLE": "SMTP 제공자 설정" + }, + "EMPTY": "사용 가능한 SMTP 제공자가 없습니다.", + "STEPS": { + "SENDGRID": {} + } + }, + "APP": { + "LIST": "애플리케이션", + "COMPLIANCE": "OIDC 준수", + "URLS": "URL", + "CONFIGURATION": "구성", + "TOKEN": "토큰 설정", + "PAGES": { + "TITLE": "애플리케이션", + "ID": "ID", + "DESCRIPTION": "여기서 애플리케이션 데이터와 구성을 편집할 수 있습니다.", + "CREATE": "애플리케이션 생성", + "CREATE_SELECT_PROJECT": "먼저 프로젝트를 선택하세요", + "CREATE_NEW_PROJECT": "또는 새 프로젝트 이름을 입력하세요", + "CREATE_DESC_TITLE": "단계별로 애플리케이션 세부사항 입력", + "CREATE_DESC_SUB": "권장 구성 설정이 자동으로 생성됩니다.", + "STATE": "상태", + "DATECREATED": "생성됨", + "DATECHANGED": "변경됨", + "URLS": "URL", + "DELETE": "앱 삭제", + "JUMPTOPROJECT": "역할, 권한 등을 구성하려면 프로젝트로 이동하세요.", + "DETAIL": { + "TITLE": "세부사항", + "STATE": { + "0": "정의되지 않음", + "1": "활성", + "2": "비활성" + } + }, + "DIALOG": { + "CONFIG": { + "TITLE": "OIDC 구성 변경" + }, + "DELETE": { + "TITLE": "앱 삭제", + "DESCRIPTION": "이 애플리케이션을 정말로 삭제하시겠습니까?" + } + }, + "NEXTSTEPS": { + "TITLE": "다음 단계", + "0": { + "TITLE": "역할 추가", + "DESC": "프로젝트 역할을 입력하세요" + }, + "1": { + "TITLE": "사용자 추가", + "DESC": "조직의 새 사용자를 추가하세요" + }, + "2": { + "TITLE": "도움말 및 지원", + "DESC": "애플리케이션 생성에 대한 문서를 읽거나 지원팀에 문의하세요" + } + } + }, + "NAMEDIALOG": { + "TITLE": "앱 이름 변경", + "DESCRIPTION": "앱의 새 이름을 입력하세요", + "NAME": "새 이름" + }, + "NAME": "이름", + "TYPE": "애플리케이션 유형", + "AUTHMETHOD": "인증 방법", + "AUTHMETHODSECTION": "인증 방법", + "GRANT": "권한 부여 유형", + "ADDITIONALORIGINS": "추가 출처", + "ADDITIONALORIGINSDESC": "리디렉션에 사용되지 않는 추가 출처를 앱에 추가하려면 여기에서 설정할 수 있습니다.", + "ORIGINS": "출처", + "NOTANORIGIN": "입력된 값이 유효한 출처가 아닙니다", + "PROSWITCH": "전문가 모드로 진행하기", + "NAMEANDTYPESECTION": "이름과 유형", + "TITLEFIRST": "애플리케이션 이름", + "TYPETITLE": "애플리케이션 유형", + "OIDC": { + "WELLKNOWN": "추가 링크는 탐색 엔드포인트에서 확인할 수 있습니다.", + "INFO": { + "ISSUER": "발급자", + "CLIENTID": "클라이언트 ID" + }, + "CURRENT": "현재 구성", + "TOKENSECTIONTITLE": "인증 토큰 옵션", + "REDIRECTSECTIONTITLE": "리디렉션 설정", + "REDIRECTTITLE": "로그인 후 리디렉션될 URI를 지정하세요.", + "POSTREDIRECTTITLE": "로그아웃 후 리디렉션 URI입니다.", + "REDIRECTDESCRIPTIONWEB": "리디렉션 URI는 https://로 시작해야 합니다. 개발 모드에서만 http://가 유효합니다.", + "REDIRECTDESCRIPTIONNATIVE": "리디렉션 URI는 http://127.0.0.1, http://[::1] 또는 http://localhost와 같은 자체 프로토콜로 시작해야 합니다.", + "REDIRECTNOTVALID": "유효하지 않은 리디렉션 URI입니다.", + "COMMAORENTERSEPERATION": "↵로 구분", + "TYPEREQUIRED": "유형은 필수 항목입니다.", + "TITLE": "OIDC 구성", + "CLIENTID": "클라이언트 ID", + "CLIENTSECRET": "클라이언트 시크릿", + "CLIENTSECRET_NOSECRET": "선택한 인증 플로우에서는 시크릿이 필요하지 않으므로 사용할 수 없습니다.", + "CLIENTSECRET_DESCRIPTION": "클라이언트 시크릿을 안전한 곳에 보관하세요. 대화 상자가 닫히면 시크릿이 사라집니다.", + "REGENERATESECRET": "클라이언트 시크릿 재생성", + "DEVMODE": "개발 모드", + "DEVMODE_ENABLED": "활성화됨", + "DEVMODE_DISABLED": "비활성화됨", + "DEVMODEDESC": "주의: 개발 모드가 활성화된 경우 리디렉션 URI가 검증되지 않습니다.", + "SKIPNATIVEAPPSUCCESSPAGE": "로그인 성공 페이지 건너뛰기", + "SKIPNATIVEAPPSUCCESSPAGE_DESCRIPTION": "네이티브 앱 로그인 후 성공 페이지를 건너뜁니다.", + "REDIRECT": "리디렉션 URI", + "REDIRECTSECTION": "리디렉션 URI", + "POSTLOGOUTREDIRECT": "로그아웃 후 리디렉션 URI", + "RESPONSESECTION": "응답 유형", + "GRANTSECTION": "권한 부여 유형", + "GRANTTITLE": "권한 부여 유형을 선택하세요. 참고: Implicit은 브라우저 기반 애플리케이션에서만 사용 가능합니다.", + "APPTYPE": { + "0": "웹", + "1": "사용자 에이전트", + "2": "네이티브" + }, + "RESPONSETYPE": "응답 유형", + "RESPONSE": { + "0": "코드", + "1": "ID 토큰", + "2": "토큰-ID 토큰" + }, + "REFRESHTOKEN": "새로 고침 토큰", + "GRANTTYPE": "권한 부여 유형", + "GRANT": { + "0": "Authorization Code", + "1": "Implicit", + "2": "Refresh Token", + "3": "Device Code", + "4": "Token Exchange" + }, + "AUTHMETHOD": { + "0": "기본", + "1": "POST", + "2": "없음", + "3": "프라이빗 키 JWT" + }, + "TOKENTYPE": "인증 토큰 유형", + "TOKENTYPE0": "Bearer Token", + "TOKENTYPE1": "JWT", + "UNSECUREREDIRECT": "정말로 이 설정을 알고 계신가요?", + "OVERVIEWSECTION": "개요", + "OVERVIEWTITLE": "구성이 완료되었습니다. 설정을 검토하세요.", + "ACCESSTOKENROLEASSERTION": "액세스 토큰에 사용자 역할 추가", + "ACCESSTOKENROLEASSERTION_DESCRIPTION": "선택 시 인증된 사용자의 요청된 역할이 액세스 토큰에 추가됩니다.", + "IDTOKENROLEASSERTION": "ID 토큰에 사용자 역할 추가", + "IDTOKENROLEASSERTION_DESCRIPTION": "선택 시 인증된 사용자의 요청된 역할이 ID 토큰에 추가됩니다.", + "IDTOKENUSERINFOASSERTION": "ID 토큰 내 사용자 정보", + "IDTOKENUSERINFOASSERTION_DESCRIPTION": "클라이언트가 ID 토큰에서 프로필, 이메일, 전화번호 및 주소 클레임을 검색할 수 있습니다.", + "CLOCKSKEW": "OP와 클라이언트 간의 시간 오차를 처리할 수 있도록 허용합니다. (0-5초) 동안 exp 클레임에 추가되고 iats, auth_time 및 nbf에서 감소됩니다.", + "RECOMMENDED": "권장", + "NOTRECOMMENDED": "권장하지 않음", + "SELECTION": { + "APPTYPE": { + "WEB": { + "TITLE": "웹", + "DESCRIPTION": ".net, PHP, Node.js, Java 등과 같은 일반 웹 애플리케이션" + }, + "NATIVE": { + "TITLE": "네이티브", + "DESCRIPTION": "모바일 앱, 데스크톱, 스마트 기기 등" + }, + "USERAGENT": { + "TITLE": "사용자 에이전트", + "DESCRIPTION": "단일 페이지 애플리케이션(SPA) 및 브라우저에서 실행되는 모든 JS 프레임워크" + } + } + } + }, + "API": { + "INFO": { + "CLIENTID": "클라이언트 ID" + }, + "REGENERATESECRET": "클라이언트 시크릿 재생성", + "SELECTION": { + "TITLE": "API", + "DESCRIPTION": "일반적인 API" + }, + "AUTHMETHOD": { + "0": "기본", + "1": "프라이빗 키 JWT" + } + }, + "SAML": { + "SELECTION": { + "TITLE": "SAML", + "DESCRIPTION": "SAML 애플리케이션" + }, + "CONFIGSECTION": "SAML 구성", + "CHOOSEMETADATASOURCE": "SAML 구성을 다음 옵션 중 하나로 제공하세요:", + "METADATAOPT1": "옵션 1. 메타데이터 파일이 위치한 URL을 지정하세요", + "METADATAOPT2": "옵션 2. 메타데이터 XML이 포함된 파일을 업로드하세요", + "METADATAOPT3": "옵션 3. ENTITYID 및 ACS URL을 제공하여 최소한의 메타데이터 파일을 실시간으로 생성", + "UPLOAD": "XML 파일 업로드", + "METADATA": "메타데이터", + "METADATAFROMFILE": "파일에서 가져온 메타데이터", + "CERTIFICATE": "SAML 인증서", + "DOWNLOADCERT": "SAML 인증서 다운로드", + "CREATEMETADATA": "메타데이터 생성", + "ENTITYID": "엔터티 ID", + "ACSURL": "ACS 엔드포인트 URL" + }, + "AUTHMETHODS": { + "CODE": { + "TITLE": "코드", + "DESCRIPTION": "토큰과 인증 코드를 교환합니다" + }, + "PKCE": { + "TITLE": "PKCE", + "DESCRIPTION": "더 높은 보안을 위해 정적 클라이언트 시크릿 대신 임의 해시 사용" + }, + "POST": { + "TITLE": "POST", + "DESCRIPTION": "폼의 일부로 client_id 및 client_secret 전송" + }, + "PK_JWT": { + "TITLE": "프라이빗 키 JWT", + "DESCRIPTION": "애플리케이션 인증을 위해 프라이빗 키 사용" + }, + "BASIC": { + "TITLE": "기본", + "DESCRIPTION": "사용자 이름과 비밀번호로 인증" + }, + "IMPLICIT": { + "TITLE": "암묵적", + "DESCRIPTION": "인증 엔드포인트에서 직접 토큰 수신" + }, + "DEVICECODE": { + "TITLE": "디바이스 코드", + "DESCRIPTION": "컴퓨터 또는 스마트폰에서 장치 인증" + }, + "CUSTOM": { + "TITLE": "사용자 정의", + "DESCRIPTION": "설정이 다른 옵션과 일치하지 않습니다." + } + }, + "TOAST": { + "REACTIVATED": "애플리케이션이 다시 활성화되었습니다.", + "DEACTIVATED": "애플리케이션이 비활성화되었습니다.", + "OIDCUPDATED": "애플리케이션이 업데이트되었습니다.", + "APIUPDATED": "애플리케이션이 업데이트되었습니다.", + "UPDATED": "애플리케이션이 업데이트되었습니다.", + "CREATED": "애플리케이션이 생성되었습니다.", + "CLIENTSECRETREGENERATED": "클라이언트 시크릿이 생성되었습니다.", + "DELETED": "애플리케이션이 삭제되었습니다.", + "CONFIGCHANGED": "변경 사항이 감지되었습니다!" + } + }, + "GENDERS": { + "0": "알 수 없음", + "1": "여성", + "2": "남성", + "3": "기타" + }, + "LANGUAGES": { + "de": "Deutsch", + "en": "English", + "es": "Español", + "fr": "Français", + "it": "Italiano", + "ja": "日本語", + "pl": "Polski", + "zh": "简体中文", + "bg": "Български", + "pt": "Portuguese", + "mk": "Македонски", + "cs": "Čeština", + "ru": "Русский", + "nl": "Nederlands", + "sv": "Svenska", + "id": "Bahasa Indonesia", + "ko": "한국어" + }, + "MEMBER": { + "ADD": "매니저 추가", + "CREATIONTYPE": "생성 유형", + "CREATIONTYPES": { + "3": "IAM", + "2": "조직", + "0": "소유 프로젝트", + "1": "부여된 프로젝트", + "4": "프로젝트" + }, + "EDITROLE": "역할 편집", + "EDITFOR": "사용자의 역할 편집: {{value}}", + "DIALOG": { + "DELETE_TITLE": "매니저 제거", + "DELETE_DESCRIPTION": "매니저를 제거하려고 합니다. 계속하시겠습니까?" + }, + "SHOWDETAILS": "세부 정보 보려면 클릭하세요." + }, + "ROLESLABEL": "역할", + "GRANTS": { + "TITLE": "권한", + "DESC": "조직의 모든 권한입니다.", + "DELETE": "권한 삭제", + "EMPTY": "권한이 없습니다", + "ADD": "권한 생성", + "ADD_BTN": "새로 추가", + "PROJECT": { + "TITLE": "권한", + "DESCRIPTION": "지정된 프로젝트에 대한 권한을 정의합니다. 권한이 있는 프로젝트와 사용자만 볼 수 있습니다." + }, + "USER": { + "TITLE": "권한", + "DESCRIPTION": "지정된 사용자에 대한 권한을 정의합니다. 권한이 있는 프로젝트와 사용자만 볼 수 있습니다." + }, + "CREATE": { + "TITLE": "권한 생성", + "DESCRIPTION": "조직, 프로젝트 및 해당 역할을 검색하세요." + }, + "EDIT": { + "TITLE": "권한 변경" + }, + "DETAIL": { + "TITLE": "권한 세부 정보", + "DESCRIPTION": "여기에서 권한의 모든 세부 정보를 볼 수 있습니다." + }, + "TOAST": { + "UPDATED": "권한이 업데이트되었습니다.", + "REMOVED": "권한이 제거되었습니다.", + "BULKREMOVED": "권한이 삭제되었습니다.", + "CANTSHOWINFO": "이 사용자가 속한 조직의 구성원이 아니기 때문에 사용자의 프로필을 볼 수 없습니다." + }, + "DIALOG": { + "DELETE_TITLE": "권한 삭제", + "DELETE_DESCRIPTION": "권한을 삭제하려고 합니다. 계속하시겠습니까?", + "BULK_DELETE_TITLE": "권한 삭제", + "BULK_DELETE_DESCRIPTION": "여러 권한을 삭제하려고 합니다. 계속하시겠습니까?" + } + }, + "CHANGES": { + "LISTTITLE": "최근 변경 사항", + "BOTTOM": "목록의 끝에 도달했습니다.", + "LOADMORE": "더 불러오기", + "ORG": { + "TITLE": "활동", + "DESCRIPTION": "조직 변경을 유발한 최신 이벤트를 볼 수 있습니다." + }, + "PROJECT": { + "TITLE": "활동", + "DESCRIPTION": "프로젝트 변경을 유발한 최신 이벤트를 볼 수 있습니다." + }, + "USER": { + "TITLE": "활동", + "DESCRIPTION": "사용자 변경을 유발한 최신 이벤트를 볼 수 있습니다." + } + } +} diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json index 5625374d1d..43b08abc9d 100644 --- a/console/src/assets/i18n/mk.json +++ b/console/src/assets/i18n/mk.json @@ -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": "Е-поштата е верифицирана", @@ -2560,7 +2562,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Додај Менаџер", diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json index 3e67d36f29..aa7008d57c 100644 --- a/console/src/assets/i18n/nl.json +++ b/console/src/assets/i18n/nl.json @@ -1620,7 +1620,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "KEYS": { "emailVerificationDoneText": "E-mail verificatie voltooid", @@ -2580,7 +2581,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Voeg een Manager toe", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index e9874d286c..0b4fd0daba 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -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", @@ -2563,7 +2565,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Dodaj managera", diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 1885358bc1..74ede27301 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -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", @@ -2558,7 +2560,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Adicionar um Gerente", diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json index 56ef096d73..775428ebcd 100644 --- a/console/src/assets/i18n/ru.json +++ b/console/src/assets/i18n/ru.json @@ -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": { @@ -2670,7 +2672,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Добавить менеджера", diff --git a/console/src/assets/i18n/sv.json b/console/src/assets/i18n/sv.json index d56384e419..d8be2e7f0f 100644 --- a/console/src/assets/i18n/sv.json +++ b/console/src/assets/i18n/sv.json @@ -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", @@ -2592,7 +2594,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "Lägg till en administratör", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index bb602d4e65..50f108533d 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -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": "电子邮件验证完成", @@ -2563,7 +2565,8 @@ "ru": "Русский", "nl": "Nederlands", "sv": "Svenska", - "id": "Bahasa Indonesia" + "id": "Bahasa Indonesia", + "ko": "한국어" }, "MEMBER": { "ADD": "添加管理者", diff --git a/docs/docs/guides/manage/customize/texts.md b/docs/docs/guides/manage/customize/texts.md index d3a3fd5299..0d4bfdd21e 100644 --- a/docs/docs/guides/manage/customize/texts.md +++ b/docs/docs/guides/manage/customize/texts.md @@ -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. diff --git a/internal/api/ui/login/static/i18n/bg.yaml b/internal/api/ui/login/static/i18n/bg.yaml index f89a672b97..ad308b859d 100644 --- a/internal/api/ui/login/static/i18n/bg.yaml +++ b/internal/api/ui/login/static/i18n/bg.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/cs.yaml b/internal/api/ui/login/static/i18n/cs.yaml index 65d70719f6..032302e3b8 100644 --- a/internal/api/ui/login/static/i18n/cs.yaml +++ b/internal/api/ui/login/static/i18n/cs.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/de.yaml b/internal/api/ui/login/static/i18n/de.yaml index d6b1d86d5e..edbeb652ce 100644 --- a/internal/api/ui/login/static/i18n/de.yaml +++ b/internal/api/ui/login/static/i18n/de.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/en.yaml b/internal/api/ui/login/static/i18n/en.yaml index 098a42f4ca..6c58b11257 100644 --- a/internal/api/ui/login/static/i18n/en.yaml +++ b/internal/api/ui/login/static/i18n/en.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/es.yaml b/internal/api/ui/login/static/i18n/es.yaml index 504f2b944b..de57fdcd85 100644 --- a/internal/api/ui/login/static/i18n/es.yaml +++ b/internal/api/ui/login/static/i18n/es.yaml @@ -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 diff --git a/internal/api/ui/login/static/i18n/fr.yaml b/internal/api/ui/login/static/i18n/fr.yaml index e79a95ccc6..8534085ae9 100644 --- a/internal/api/ui/login/static/i18n/fr.yaml +++ b/internal/api/ui/login/static/i18n/fr.yaml @@ -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 diff --git a/internal/api/ui/login/static/i18n/hu.yaml b/internal/api/ui/login/static/i18n/hu.yaml index f10829b493..80ed98945c 100644 --- a/internal/api/ui/login/static/i18n/hu.yaml +++ b/internal/api/ui/login/static/i18n/hu.yaml @@ -234,6 +234,7 @@ RegistrationUser: Swedish: Svéd Indonesian: Indonéz Hungarian: Magyar + Korean: 한국어 GenderLabel: Nem Female: Nő 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: diff --git a/internal/api/ui/login/static/i18n/id.yaml b/internal/api/ui/login/static/i18n/id.yaml index a8bf57f467..63deb41229 100644 --- a/internal/api/ui/login/static/i18n/id.yaml +++ b/internal/api/ui/login/static/i18n/id.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/it.yaml b/internal/api/ui/login/static/i18n/it.yaml index 62b49e8bca..46e74d3b13 100644 --- a/internal/api/ui/login/static/i18n/it.yaml +++ b/internal/api/ui/login/static/i18n/it.yaml @@ -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 diff --git a/internal/api/ui/login/static/i18n/ja.yaml b/internal/api/ui/login/static/i18n/ja.yaml index 68899b5c65..9ec99eb912 100644 --- a/internal/api/ui/login/static/i18n/ja.yaml +++ b/internal/api/ui/login/static/i18n/ja.yaml @@ -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: デバイス認証 diff --git a/internal/api/ui/login/static/i18n/ko.yaml b/internal/api/ui/login/static/i18n/ko.yaml new file mode 100644 index 0000000000..e62cfcb8b5 --- /dev/null +++ b/internal/api/ui/login/static/i18n/ko.yaml @@ -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: (선택 사항) diff --git a/internal/api/ui/login/static/i18n/mk.yaml b/internal/api/ui/login/static/i18n/mk.yaml index 2424701077..dbb988a0a6 100644 --- a/internal/api/ui/login/static/i18n/mk.yaml +++ b/internal/api/ui/login/static/i18n/mk.yaml @@ -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: Овластување преку уред diff --git a/internal/api/ui/login/static/i18n/nl.yaml b/internal/api/ui/login/static/i18n/nl.yaml index 017e1a52d8..3bbcee94b6 100644 --- a/internal/api/ui/login/static/i18n/nl.yaml +++ b/internal/api/ui/login/static/i18n/nl.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/pl.yaml b/internal/api/ui/login/static/i18n/pl.yaml index 367da1a2cd..2c8b4fddf0 100644 --- a/internal/api/ui/login/static/i18n/pl.yaml +++ b/internal/api/ui/login/static/i18n/pl.yaml @@ -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 diff --git a/internal/api/ui/login/static/i18n/pt.yaml b/internal/api/ui/login/static/i18n/pt.yaml index b0c6cb6c8e..f03f120ed8 100644 --- a/internal/api/ui/login/static/i18n/pt.yaml +++ b/internal/api/ui/login/static/i18n/pt.yaml @@ -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 diff --git a/internal/api/ui/login/static/i18n/ru.yaml b/internal/api/ui/login/static/i18n/ru.yaml index e6edc967fb..03239e0612 100644 --- a/internal/api/ui/login/static/i18n/ru.yaml +++ b/internal/api/ui/login/static/i18n/ru.yaml @@ -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: Авторизация устройства diff --git a/internal/api/ui/login/static/i18n/sv.yaml b/internal/api/ui/login/static/i18n/sv.yaml index 6d1d5ef8ac..26fee23551 100644 --- a/internal/api/ui/login/static/i18n/sv.yaml +++ b/internal/api/ui/login/static/i18n/sv.yaml @@ -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: diff --git a/internal/api/ui/login/static/i18n/zh.yaml b/internal/api/ui/login/static/i18n/zh.yaml index dbbe969b6e..79db3c020e 100644 --- a/internal/api/ui/login/static/i18n/zh.yaml +++ b/internal/api/ui/login/static/i18n/zh.yaml @@ -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: diff --git a/internal/api/ui/login/static/templates/external_not_found_option.html b/internal/api/ui/login/static/templates/external_not_found_option.html index 9173671b16..33bcaeb4e0 100644 --- a/internal/api/ui/login/static/templates/external_not_found_option.html +++ b/internal/api/ui/login/static/templates/external_not_found_option.html @@ -98,6 +98,8 @@ + diff --git a/internal/notification/static/i18n/ko.yaml b/internal/notification/static/i18n/ko.yaml new file mode 100644 index 0000000000..6bc160e3ec --- /dev/null +++ b/internal/notification/static/i18n/ko.yaml @@ -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: 초대 수락 diff --git a/internal/static/i18n/ko.yaml b/internal/static/i18n/ko.yaml new file mode 100644 index 0000000000..fda5171de2 --- /dev/null +++ b/internal/static/i18n/ko.yaml @@ -0,0 +1,1406 @@ +Errors: + Internal: 내부 오류가 발생했습니다 + NoChangesFound: 변경 사항 없음 + OriginNotAllowed: 이 "Origin"은 허용되지 않습니다 + IDMissing: ID가 누락되었습니다 + ResourceOwnerMissing: 리소스 소유 조직이 누락되었습니다 + RemoveFailed: 제거할 수 없습니다 + ProjectionName: + Invalid: 잘못된 투영 이름입니다 + Assets: + EmptyKey: 자산 키가 비어 있습니다 + Store: + NotInitialized: 자산 저장소가 초기화되지 않았습니다 + NotConfigured: 자산 저장소가 구성되지 않았습니다 + Bucket: + Internal: 버킷 생성 중 내부 오류 발생 + AlreadyExists: 버킷이 이미 존재합니다 + CreateFailed: 버킷이 생성되지 않았습니다 + ListFailed: 버킷을 읽을 수 없습니다 + RemoveFailed: 버킷이 삭제되지 않았습니다 + SetPublicFailed: 버킷을 공개로 설정할 수 없습니다 + Object: + PutFailed: 객체가 생성되지 않았습니다 + GetFailed: 객체를 읽을 수 없습니다 + NotFound: 객체를 찾을 수 없습니다 + PresignedTokenFailed: 서명된 토큰을 생성할 수 없습니다 + ListFailed: 객체 목록을 읽을 수 없습니다 + RemoveFailed: 객체를 삭제할 수 없습니다 + Limit: + ExceedsDefault: 제한이 기본 제한을 초과합니다 + Limits: + NotFound: 제한을 찾을 수 없습니다 + NoneSpecified: 지정된 제한이 없습니다 + Instance: + Blocked: 인스턴스가 차단되었습니다 + Restrictions: + NoneSpecified: 지정된 제한이 없습니다 + DefaultLanguageMustBeAllowed: 기본 언어는 허용되어야 합니다 + Language: + NotParsed: 언어를 구문 분석할 수 없습니다 + NotSupported: 지원되지 않는 언어입니다 + NotAllowed: 허용되지 않는 언어입니다 + Undefined: 언어가 정의되지 않았습니다 + Duplicate: 중복된 언어가 있습니다 + OIDCSettings: + NotFound: OIDC 구성을 찾을 수 없습니다 + AlreadyExists: OIDC 구성이 이미 존재합니다 + SecretGenerator: + AlreadyExists: 시크릿 생성기가 이미 존재합니다 + TypeMissing: 시크릿 생성기 유형이 누락되었습니다 + NotFound: 시크릿 생성기를 찾을 수 없습니다 + SMSConfig: + NotFound: SMS 구성을 찾을 수 없습니다 + AlreadyActive: SMS 구성이 이미 활성화되었습니다 + AlreadyDeactivated: SMS 구성이 이미 비활성화되었습니다 + NotExternalVerification: SMS 구성은 코드 검증을 지원하지 않습니다 + SMTP: + NotEmailMessage: 메시지가 이메일 메시지가 아닙니다 + RequiredAttributes: subject, recipients 및 content가 설정되어야 하지만 일부 또는 모두 비어 있습니다 + CouldNotSplit: smtp 연결을 위해 호스트와 포트를 분할할 수 없습니다 + CouldNotDial: SMTP 서버에 연결할 수 없습니다. 포트 또는 방화벽 문제를 확인하십시오... + CouldNotDialTLS: TLS를 사용하여 SMTP 서버에 연결할 수 없습니다. 포트 또는 방화벽 문제를 확인하십시오... + CouldNotCreateClient: smtp 클라이언트를 생성할 수 없습니다 + CouldNotStartTLS: TLS를 시작할 수 없습니다 + CouldNotAuth: smtp 인증을 추가할 수 없습니다. 사용자 이름과 비밀번호가 정확한지 확인하십시오. 정확하다면 제공자가 ZITADEL에서 지원하지 않는 인증 방법을 요구할 수 있습니다 + CouldNotSetSender: 발신자를 설정할 수 없습니다 + CouldNotSetRecipient: 수신자를 설정할 수 없습니다 + SMTPConfig: + TestPassword: 테스트할 비밀번호가 없습니다 + NotFound: SMTP 구성을 찾을 수 없습니다 + AlreadyExists: SMTP 구성이 이미 존재합니다 + AlreadyDeactivated: SMTP 구성이 이미 비활성화되었습니다 + SenderAdressNotCustomDomain: 발신자 주소는 인스턴스에서 사용자 정의 도메인으로 구성되어야 합니다 + TestEmailNotFound: 테스트할 이메일 주소가 없습니다 + Notification: + NoDomain: 메시지에 대한 도메인을 찾을 수 없습니다 + User: + NotFound: 사용자를 찾을 수 없습니다 + AlreadyExists: 사용자가 이미 존재합니다 + NotFoundOnOrg: 선택한 조직에서 사용자를 찾을 수 없습니다 + NotAllowedOrg: 사용자가 필수 조직의 구성원이 아닙니다 + UserIDMissing: 사용자 ID가 누락되었습니다 + UserIDWrong: "요청한 사용자와 인증된 사용자가 일치하지 않습니다" + DomainPolicyNil: 조직 정책이 비어 있습니다 + EmailAsUsernameNotAllowed: 이메일을 사용자 이름으로 사용할 수 없습니다 + Invalid: 사용자 데이터가 잘못되었습니다 + DomainNotAllowedAsUsername: 도메인이 이미 예약되어 사용할 수 없습니다 + AlreadyInactive: 사용자가 이미 비활성 상태입니다 + NotInactive: 사용자가 비활성 상태가 아닙니다 + CantDeactivateInitial: 초기 상태의 사용자는 비활성화할 수 없으며 삭제만 가능합니다 + ShouldBeActiveOrInitial: 사용자가 활성 상태이거나 초기 상태가 아닙니다 + AlreadyInitialised: 사용자가 이미 초기화되었습니다 + NotInitialised: 사용자가 아직 초기화되지 않았습니다 + NotLocked: 사용자가 잠겨 있지 않습니다 + NoChanges: 변경 사항이 없습니다 + InitCodeNotFound: 초기화 코드를 찾을 수 없습니다 + UsernameNotChanged: 사용자 이름이 변경되지 않았습니다 + InvalidURLTemplate: URL 템플릿이 잘못되었습니다 + 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: 주소가 변경되지 않았습니다 + Machine: + Key: + NotFound: 머신 키를 찾을 수 없습니다 + AlreadyExisting: 머신 키가 이미 존재합니다 + Invalid: 공개 키가 PKIX 형식의 PEM 인코딩을 따르는 유효한 RSA 공개 키가 아닙니다 + Secret: + NotExisting: 시크릿이 존재하지 않습니다 + Invalid: 시크릿이 잘못되었습니다 + CouldNotGenerate: 시크릿을 생성할 수 없습니다 + PAT: + NotFound: 개인 액세스 토큰을 찾을 수 없습니다 + NotHuman: 사용자는 개인이어야 합니다 + NotMachine: 사용자는 기술적이어야 합니다 + WrongType: 이 사용자 유형에는 허용되지 않습니다 + NotAllowedToLink: 사용자는 외부 로그인 제공자와 링크할 수 없습니다 + Username: + AlreadyExists: 사용자 이름이 이미 사용 중입니다 + Reserved: 사용자 이름이 이미 사용 중입니다 + Empty: 사용자 이름이 비어 있습니다 + Code: + Empty: 코드가 비어 있습니다 + NotFound: 코드를 찾을 수 없습니다 + Expired: 코드가 만료되었습니다 + GeneratorAlgNotSupported: 지원되지 않는 생성 알고리즘 + Invalid: 코드가 잘못되었습니다 + Password: + NotFound: 비밀번호를 찾을 수 없습니다 + Empty: 비밀번호가 비어 있습니다 + Invalid: 비밀번호가 잘못되었습니다 + NotSet: 사용자가 비밀번호를 설정하지 않았습니다 + NotChanged: 새 비밀번호는 현재 비밀번호와 다르지 않아야 합니다 + NotSupported: 비밀번호 해시 인코딩이 지원되지 않습니다. 자세한 내용은 https://zitadel.com/docs/concepts/architecture/secrets#hashed-secrets를 참조하세요 + PasswordComplexityPolicy: + NotFound: 비밀번호 정책을 찾을 수 없습니다 + MinLength: 비밀번호가 너무 짧습니다 + MinLengthNotAllowed: 지정된 최소 길이는 허용되지 않습니다 + HasLower: 비밀번호에는 소문자가 포함되어야 합니다 + HasUpper: 비밀번호에는 대문자가 포함되어야 합니다 + HasNumber: 비밀번호에는 숫자가 포함되어야 합니다 + HasSymbol: 비밀번호에는 기호가 포함되어야 합니다 + ExternalIDP: + Invalid: 외부 IDP가 잘못되었습니다 + IDPConfigNotExisting: 이 조직에 대해 유효하지 않은 IDP 제공자입니다 + NotAllowed: 외부 IDP가 허용되지 않습니다 + MinimumExternalIDPNeeded: 최소 하나의 IDP가 추가되어야 합니다 + AlreadyExists: 외부 IDP가 이미 사용 중입니다 + NotFound: 외부 IDP를 찾을 수 없습니다 + LoginFailed: 외부 IDP에서 로그인에 실패했습니다 + MFA: + OTP: + AlreadyReady: 다중 요소 OTP(일회용 비밀번호)가 이미 설정되었습니다 + NotExisting: 다중 요소 OTP(일회용 비밀번호)가 존재하지 않습니다 + NotReady: 다중 요소 OTP(일회용 비밀번호)가 준비되지 않았습니다 + InvalidCode: 코드가 잘못되었습니다 + U2F: + NotExisting: U2F가 존재하지 않습니다 + Passwordless: + NotExisting: 패스워드리스가 존재하지 않습니다 + WebAuthN: + NotFound: WebAuthN 토큰을 찾을 수 없습니다 + BeginRegisterFailed: WebAuthN 등록 시작에 실패했습니다 + MarshalError: 데이터 직렬화 오류 발생 + ErrorOnParseCredential: 자격 증명 데이터 구문 분석 오류 + CreateCredentialFailed: 자격 증명 생성 오류 + BeginLoginFailed: WebAuthN 로그인 시작에 실패했습니다 + ValidateLoginFailed: 로그인 자격 증명 확인 오류 + CloneWarning: 자격 증명이 복제될 수 있습니다 + RefreshToken: + Invalid: 리프레시 토큰이 잘못되었습니다 + NotFound: 리프레시 토큰을 찾을 수 없습니다 + Instance: + NotFound: 인스턴스를 찾을 수 없습니다 + AlreadyExists: 인스턴스가 이미 존재합니다 + NotChanged: 인스턴스가 변경되지 않았습니다 + Org: + AlreadyExists: 조직 이름이 이미 사용 중입니다 + Invalid: 조직이 유효하지 않습니다 + AlreadyDeactivated: 조직이 이미 비활성화되었습니다 + AlreadyActive: 조직이 이미 활성화되었습니다 + Empty: 조직이 비어 있습니다 + NotFound: 조직을 찾을 수 없습니다 + NotChanged: 조직이 변경되지 않았습니다 + DefaultOrgNotDeletable: 기본 조직은 삭제할 수 없습니다 + ZitadelOrgNotDeletable: ZITADEL 프로젝트가 포함된 조직은 삭제할 수 없습니다 + InvalidDomain: 유효하지 않은 도메인입니다 + DomainMissing: 도메인이 누락되었습니다 + DomainNotOnOrg: 조직에 도메인이 존재하지 않습니다 + DomainNotVerified: 도메인이 인증되지 않았습니다 + DomainAlreadyVerified: 도메인이 이미 인증되었습니다 + DomainVerificationTypeInvalid: 도메인 인증 유형이 유효하지 않습니다 + DomainVerificationMissing: 도메인 인증이 아직 시작되지 않았습니다 + DomainVerificationFailed: 도메인 인증에 실패했습니다 + DomainVerificationTXTNotFound: 도메인에서 _zitadel-challenge TXT 레코드를 찾을 수 없습니다. DNS 서버에 추가했는지 확인하거나 새로운 레코드가 전파될 때까지 기다리세요 + DomainVerificationTXTNoMatch: 도메인에서 _zitadel-challenge TXT 레코드를 찾았으나 올바른 토큰 텍스트가 포함되어 있지 않습니다. DNS 서버에 올바른 토큰을 추가했는지 확인하거나 새로운 레코드가 전파될 때까지 기다리세요 + DomainVerificationHTTPNotFound: 예상되는 URL에 챌린지를 포함하는 파일을 찾을 수 없습니다. 파일이 올바른 위치에 읽기 권한으로 업로드되었는지 확인하세요 + DomainVerificationHTTPNoMatch: 예상되는 URL에 챌린지 파일이 있지만 올바른 토큰 텍스트가 포함되어 있지 않습니다. 파일의 내용을 확인하세요 + DomainVerificationTimeout: DNS 서버에 대한 쿼리에서 시간 초과가 발생했습니다 + PrimaryDomainNotDeletable: 기본 도메인은 삭제할 수 없습니다 + DomainNotFound: 도메인을 찾을 수 없습니다 + MemberIDMissing: 구성원 ID가 누락되었습니다 + MemberNotFound: 조직 구성원을 찾을 수 없습니다 + InvalidMember: 조직 구성원이 유효하지 않습니다 + UserIDMissing: 사용자 ID가 누락되었습니다 + PolicyAlreadyExists: 정책이 이미 존재합니다 + PolicyNotExisting: 정책이 존재하지 않습니다 + IdpInvalid: IDP 설정이 유효하지 않습니다 + IdpNotExisting: IDP 설정이 존재하지 않습니다 + OIDCConfigInvalid: OIDC IDP 설정이 유효하지 않습니다 + IdpIsNotOIDC: IDP 설정이 OIDC 유형이 아닙니다 + Domain: + AlreadyExists: 도메인이 이미 존재합니다 + InvalidCharacter: 도메인에는 영숫자, ., -만 허용됩니다 + EmptyString: 유효하지 않은 문자들이 비어 있는 문자열로 대체되었고 결과 도메인이 비어 있습니다 + IDP: + InvalidSearchQuery: 잘못된 검색 쿼리입니다 + ClientIDMissing: ClientID가 누락되었습니다 + TeamIDMissing: TeamID가 누락되었습니다 + KeyIDMissing: KeyID가 누락되었습니다 + PrivateKeyMissing: 개인 키가 누락되었습니다 + LoginPolicy: + NotFound: 로그인 정책을 찾을 수 없습니다 + Invalid: 로그인 정책이 유효하지 않습니다 + RedirectURIInvalid: 기본 리디렉트 URI가 유효하지 않습니다 + NotExisting: 로그인 정책이 존재하지 않습니다 + AlreadyExists: 로그인 정책이 이미 존재합니다 + IdpProviderAlreadyExisting: IDP 제공자가 이미 존재합니다 + IdpProviderNotExisting: IDP 제공자가 존재하지 않습니다 + RegistrationNotAllowed: 등록이 허용되지 않습니다 + UsernamePasswordNotAllowed: 사용자 이름/비밀번호로 로그인할 수 없습니다 + MFA: + AlreadyExists: 다중 인증이 이미 존재합니다 + NotExisting: 다중 인증이 존재하지 않습니다 + Unspecified: 다중 인증이 유효하지 않습니다 + MailTemplate: + NotFound: 기본 메일 템플릿을 찾을 수 없습니다 + NotChanged: 기본 메일 템플릿이 변경되지 않았습니다 + AlreadyExists: 기본 메일 템플릿이 이미 존재합니다 + Invalid: 기본 메일 템플릿이 유효하지 않습니다 + CustomMessageText: + NotFound: 기본 메시지 텍스트를 찾을 수 없습니다 + NotChanged: 기본 메시지 텍스트가 변경되지 않았습니다 + AlreadyExists: 기본 메시지 텍스트가 이미 존재합니다 + Invalid: 기본 메시지 텍스트가 유효하지 않습니다 + PasswordComplexityPolicy: + NotFound: 비밀번호 복잡성 정책을 찾을 수 없습니다 + Empty: 비밀번호 복잡성 정책이 비어 있습니다 + NotExisting: 비밀번호 복잡성 정책이 존재하지 않습니다 + AlreadyExists: 비밀번호 복잡성 정책이 이미 존재합니다 + PasswordLockoutPolicy: + NotFound: 비밀번호 잠금 정책을 찾을 수 없습니다 + Empty: 비밀번호 잠금 정책이 비어 있습니다 + NotExisting: 비밀번호 잠금 정책이 존재하지 않습니다 + AlreadyExists: 비밀번호 잠금 정책이 이미 존재합니다 + PasswordAgePolicy: + NotFound: 비밀번호 만료 정책을 찾을 수 없습니다 + Empty: 비밀번호 만료 정책이 비어 있습니다 + NotExisting: 비밀번호 만료 정책이 존재하지 않습니다 + AlreadyExists: 비밀번호 만료 정책이 이미 존재합니다 + OrgIAMPolicy: + Empty: 조직 IAM 정책이 비어 있습니다 + NotExisting: 조직 IAM 정책이 존재하지 않습니다 + AlreadyExists: 조직 IAM 정책이 이미 존재합니다 + NotificationPolicy: + NotFound: 알림 정책을 찾을 수 없습니다 + NotChanged: 알림 정책이 변경되지 않았습니다 + AlreadyExists: 알림 정책이 이미 존재합니다 + LabelPolicy: + NotFound: 개인 라벨 정책을 찾을 수 없습니다 + NotChanged: 개인 라벨 정책이 변경되지 않았습니다 + Project: + ProjectIDMissing: 프로젝트 ID가 누락되었습니다 + AlreadyExists: 조직에 프로젝트가 이미 존재합니다 + OrgNotExisting: 조직이 존재하지 않습니다 + UserNotExisting: 사용자가 존재하지 않습니다 + CouldNotGenerateClientSecret: 클라이언트 시크릿을 생성할 수 없습니다 + Invalid: 프로젝트가 유효하지 않습니다 + NotActive: 프로젝트가 활성 상태가 아닙니다 + NotInactive: 프로젝트가 비활성화 상태가 아닙니다 + NotFound: 프로젝트를 찾을 수 없습니다 + UserIDMissing: 사용자 ID가 누락되었습니다 + Member: + NotFound: 프로젝트 구성원을 찾을 수 없습니다 + Invalid: 프로젝트 구성원이 유효하지 않습니다 + AlreadyExists: 프로젝트 구성원이 이미 존재합니다 + NotExisting: 프로젝트 구성원이 존재하지 않습니다 + MinimumOneRoleNeeded: 최소 하나의 역할이 추가되어야 합니다 + Role: + AlreadyExists: 역할이 이미 존재합니다 + Invalid: 역할이 유효하지 않습니다 + NotExisting: 역할이 존재하지 않습니다 + IDMissing: ID가 누락되었습니다 + App: + AlreadyExists: 애플리케이션이 이미 존재합니다 + NotFound: 애플리케이션을 찾을 수 없습니다 + Invalid: 애플리케이션이 유효하지 않습니다 + NotExisting: 애플리케이션이 존재하지 않습니다 + NotActive: 애플리케이션이 활성 상태가 아닙니다 + NotInactive: 애플리케이션이 비활성 상태가 아닙니다 + OIDCConfigInvalid: OIDC 설정이 유효하지 않습니다 + APIConfigInvalid: API 설정이 유효하지 않습니다 + SAMLConfigInvalid: SAML 설정이 유효하지 않습니다 + IsNotOIDC: 애플리케이션이 OIDC 유형이 아닙니다 + IsNotAPI: 애플리케이션이 API 유형이 아닙니다 + IsNotSAML: 애플리케이션이 SAML 유형이 아닙니다 + SAMLMetadataMissing: SAML 메타데이터가 누락되었습니다 + SAMLMetadataFormat: SAML 메타데이터 형식 오류 + SAMLEntityIDAlreadyExisting: SAML EntityID가 이미 존재합니다 + OIDCAuthMethodNoSecret: 선택한 OIDC 인증 방법에는 시크릿이 필요하지 않습니다 + APIAuthMethodNoSecret: 선택한 API 인증 방법에는 시크릿이 필요하지 않습니다 + AuthMethodNoPrivateKeyJWT: 선택한 인증 방법에는 키가 필요하지 않습니다 + ClientSecretInvalid: 클라이언트 시크릿이 유효하지 않습니다 + Key: + AlreadyExisting: 애플리케이션 키가 이미 존재합니다 + NotFound: 애플리케이션 키를 찾을 수 없습니다 + RequiredFieldsMissing: 필요한 필드가 일부 누락되었습니다 + Grant: + AlreadyExists: 프로젝트 권한이 이미 존재합니다 + NotFound: 권한을 찾을 수 없습니다 + Invalid: 프로젝트 권한이 유효하지 않습니다 + NotExisting: 프로젝트 권한이 존재하지 않습니다 + HasNotExistingRole: 프로젝트에 존재하지 않는 역할이 있습니다 + NotActive: 프로젝트 권한이 활성 상태가 아닙니다 + NotInactive: 프로젝트 권한이 비활성 상태가 아닙니다 + IAM: + NotFound: 인스턴스를 찾을 수 없습니다. 도메인이 올바른지 확인하십시오. https://zitadel.com/docs/apis/introduction#domains 를 참조하세요 + Member: + RolesNotChanged: 역할이 변경되지 않았습니다 + MemberInvalid: 구성원이 유효하지 않습니다 + MemberAlreadyExisting: 구성원이 이미 존재합니다 + MemberNotExisting: 구성원이 존재하지 않습니다 + IDMissing: ID가 누락되었습니다 + IAMProjectIDMissing: IAM 프로젝트 ID가 누락되었습니다 + IamProjectAlreadySet: IAM 프로젝트 ID가 이미 설정되었습니다 + IdpInvalid: IDP 설정이 유효하지 않습니다 + IdpNotExisting: IDP 설정이 존재하지 않습니다 + OIDCConfigInvalid: OIDC IDP 설정이 유효하지 않습니다 + IdpIsNotOIDC: IDP 설정이 OIDC 유형이 아닙니다 + LoginPolicyInvalid: 로그인 정책이 유효하지 않습니다 + LoginPolicyNotExisting: 로그인 정책이 존재하지 않습니다 + IdpProviderInvalid: IDP 제공자가 유효하지 않습니다 + LoginPolicy: + NotFound: 기본 로그인 정책을 찾을 수 없습니다 + NotChanged: 기본 로그인 정책이 변경되지 않았습니다 + NotExisting: 기본 로그인 정책이 존재하지 않습니다 + AlreadyExists: 기본 로그인 정책이 이미 존재합니다 + RedirectURIInvalid: 기본 리디렉트 URI가 유효하지 않습니다 + MFA: + AlreadyExists: 다중 인증이 이미 존재합니다 + NotExisting: 다중 인증이 존재하지 않습니다 + Unspecified: 다중 인증이 유효하지 않습니다 + IDP: + AlreadyExists: IDP 제공자가 이미 존재합니다 + NotExisting: IDP 제공자가 존재하지 않습니다 + Invalid: IDP 제공자가 유효하지 않습니다 + IDPConfig: + AlreadyExists: IDP 설정이 이미 존재합니다 + NotInactive: IDP 설정이 비활성화 상태가 아닙니다 + NotActive: IDP 설정이 활성 상태가 아닙니다 + LabelPolicy: + NotFound: 기본 개인 라벨 정책을 찾을 수 없습니다 + NotChanged: 기본 개인 라벨 정책이 변경되지 않았습니다 + MailTemplate: + NotFound: 기본 메일 템플릿을 찾을 수 없습니다 + NotChanged: 기본 메일 템플릿이 변경되지 않았습니다 + AlreadyExists: 기본 메일 템플릿이 이미 존재합니다 + Invalid: 기본 메일 템플릿이 유효하지 않습니다 + CustomMessageText: + NotFound: 기본 메시지 텍스트를 찾을 수 없습니다 + NotChanged: 기본 메시지 텍스트가 변경되지 않았습니다 + AlreadyExists: 기본 메시지 텍스트가 이미 존재합니다 + Invalid: 기본 메시지 텍스트가 유효하지 않습니다 + PasswordComplexityPolicy: + NotFound: 기본 비밀번호 복잡성 정책을 찾을 수 없습니다 + NotExisting: 기본 비밀번호 복잡성 정책이 존재하지 않습니다 + AlreadyExists: 기본 비밀번호 복잡성 정책이 이미 존재합니다 + Empty: 기본 비밀번호 복잡성 정책이 비어 있습니다 + NotChanged: 기본 비밀번호 복잡성 정책이 변경되지 않았습니다 + PasswordAgePolicy: + NotFound: 기본 비밀번호 만료 정책을 찾을 수 없습니다 + NotExisting: 기본 비밀번호 만료 정책이 존재하지 않습니다 + AlreadyExists: 기본 비밀번호 만료 정책이 이미 존재합니다 + Empty: 기본 비밀번호 만료 정책이 비어 있습니다 + NotChanged: 기본 비밀번호 만료 정책이 변경되지 않았습니다 + PasswordLockoutPolicy: + NotFound: 기본 비밀번호 잠금 정책을 찾을 수 없습니다 + NotExisting: 기본 비밀번호 잠금 정책이 존재하지 않습니다 + AlreadyExists: 기본 비밀번호 잠금 정책이 이미 존재합니다 + Empty: 기본 비밀번호 잠금 정책이 비어 있습니다 + NotChanged: 기본 비밀번호 잠금 정책이 변경되지 않았습니다 + DomainPolicy: + NotFound: 조직 IAM 정책을 찾을 수 없습니다 + Empty: 조직 IAM 정책이 비어 있습니다 + NotExisting: 조직 IAM 정책이 존재하지 않습니다 + AlreadyExists: 조직 IAM 정책이 이미 존재합니다 + NotChanged: 조직 IAM 정책이 변경되지 않았습니다 + NotificationPolicy: + NotFound: 기본 알림 정책을 찾을 수 없습니다 + NotChanged: 기본 알림 정책이 변경되지 않았습니다 + AlreadyExists: 기본 알림 정책이 이미 존재합니다 + Policy: + AlreadyExists: 정책이 이미 존재합니다 + Label: + Invalid: + PrimaryColor: 기본 색상이 유효한 16진수 색상 값이 아닙니다 + BackgroundColor: 배경 색상이 유효한 16진수 색상 값이 아닙니다 + WarnColor: 경고 색상이 유효한 16진수 색상 값이 아닙니다 + FontColor: 글꼴 색상이 유효한 16진수 색상 값이 아닙니다 + PrimaryColorDark: 기본 색상(다크 모드)이 유효한 16진수 색상 값이 아닙니다 + BackgroundColorDark: 배경 색상(다크 모드)이 유효한 16진수 색상 값이 아닙니다 + WarnColorDark: 경고 색상(다크 모드)이 유효한 16진수 색상 값이 아닙니다 + FontColorDark: 글꼴 색상(다크 모드)이 유효한 16진수 색상 값이 아닙니다 + UserGrant: + AlreadyExists: 사용자 권한이 이미 존재합니다 + NotFound: 사용자 권한을 찾을 수 없습니다 + Invalid: 사용자 권한이 유효하지 않습니다 + NotChanged: 사용자 권한이 변경되지 않았습니다 + IDMissing: ID가 누락되었습니다 + NotActive: 사용자 권한이 활성 상태가 아닙니다 + NotInactive: 사용자 권한이 비활성 상태가 아닙니다 + NoPermissionForProject: 사용자가 이 프로젝트에 대한 권한이 없습니다 + RoleKeyNotFound: 역할을 찾을 수 없습니다 + Member: + AlreadyExists: 구성원이 이미 존재합니다 + IDPConfig: + AlreadyExists: 동일한 이름의 IDP 설정이 이미 존재합니다 + NotExisting: IDP 설정이 존재하지 않습니다 + Changes: + NotFound: 기록을 찾을 수 없습니다 + AuditRetention: 기록이 감사 로그 보존 기간을 초과했습니다 + Token: + NotFound: 토큰을 찾을 수 없습니다 + Invalid: 토큰이 유효하지 않습니다 + UserSession: + NotFound: 사용자 세션을 찾을 수 없습니다 + Key: + NotFound: 키를 찾을 수 없습니다 + ExpireBeforeNow: 만료일이 이미 경과했습니다 + Login: + LoginPolicy: + MFA: + ForceAndNotConfigured: 다중 인증이 필수로 설정되었지만 가능한 제공자가 구성되지 않았습니다. 시스템 관리자에게 문의하십시오. + Step: + Started: + AlreadyExists: 이미 시작된 단계가 존재합니다 + Done: + AlreadyExists: 이미 완료된 단계가 존재합니다 + CustomText: + AlreadyExists: 사용자 정의 텍스트가 이미 존재합니다 + Invalid: 사용자 정의 텍스트가 유효하지 않습니다 + NotFound: 사용자 정의 텍스트를 찾을 수 없습니다 + TranslationFile: + ReadError: 번역 파일을 읽는 중 오류 발생 + MergeError: 번역 파일을 사용자 정의 번역과 병합할 수 없습니다 + NotFound: 번역 파일이 존재하지 않습니다 + Metadata: + NotFound: 메타데이터를 찾을 수 없습니다 + NoData: 메타데이터 목록이 비어 있습니다 + Invalid: 메타데이터가 유효하지 않습니다 + KeyNotExisting: 하나 이상의 키가 존재하지 않습니다 + Action: + Invalid: 작업이 유효하지 않습니다 + NotFound: 작업을 찾을 수 없습니다 + NotActive: 작업이 활성화되지 않았습니다 + NotInactive: 작업이 비활성화되지 않았습니다 + MaxAllowed: 추가 활성 작업이 허용되지 않습니다 + NotEnabled: "\"작업\" 기능이 활성화되지 않았습니다" + Flow: + FlowTypeMissing: FlowType이 누락되었습니다 + Empty: 플로우가 이미 비어 있습니다 + WrongTriggerType: TriggerType이 유효하지 않습니다 + NoChanges: 변경 사항이 없습니다 + ActionIDsNotExist: ActionIDs가 존재하지 않습니다 + Query: + CloseRows: SQL 문을 완료할 수 없습니다 + SQLStatement: SQL 문을 생성할 수 없습니다 + InvalidRequest: 요청이 유효하지 않습니다 + TooManyNestingLevels: 쿼리 중첩 수준이 너무 많습니다 (최대 20) + LimitExceeded: 제한을 초과했습니다 + Quota: + AlreadyExists: 이 단위에 대한 할당량이 이미 존재합니다 + NotFound: 이 단위에 대한 할당량을 찾을 수 없습니다 + Invalid: + CallURL: 할당량 호출 URL이 유효하지 않습니다 + Percent: 할당량 백분율이 1 미만입니다 + Unimplemented: 이 단위에 대해 할당량이 구현되지 않았습니다 + Amount: 할당량 금액이 1 미만입니다 + ResetInterval: 할당량 재설정 간격이 1분보다 짧습니다 + Noop: 알림 없는 무제한 할당량은 효과가 없습니다 + Access: + Exhausted: 인증된 요청에 대한 할당량이 소진되었습니다 + Execution: + Exhausted: 실행 시간에 대한 할당량이 소진되었습니다 + LogStore: + Access: + StorageFailed: 액세스 로그를 데이터베이스에 저장하지 못했습니다 + ScanFailed: 인증된 요청 사용량 조회 실패 + Execution: + StorageFailed: 작업 실행 로그를 데이터베이스에 저장하지 못했습니다 + ScanFailed: 작업 실행 시간 사용량 조회 실패 + Session: + NotExisting: 세션이 존재하지 않습니다 + Terminated: 세션이 이미 종료되었습니다 + Expired: 세션이 만료되었습니다 + PositiveLifetime: 세션 수명은 0보다 작아서는 안 됩니다 + Token: + Invalid: 세션 토큰이 유효하지 않습니다 + WebAuthN: + NoChallenge: WebAuthN 챌린지가 없는 세션 + Intent: + IDPMissing: 요청에서 IDP ID가 누락되었습니다 + IDPInvalid: 요청에 대한 IDP가 유효하지 않습니다 + ResponseInvalid: IDP 응답이 유효하지 않습니다 + MissingSingleMappingAttribute: IDP 응답에 매핑 속성이 포함되어 있지 않거나 값이 하나 이상 있습니다 + SuccessURLMissing: 요청에 성공 URL이 누락되었습니다 + FailureURLMissing: 요청에 실패 URL이 누락되었습니다 + StateMissing: 요청에 상태 매개변수가 누락되었습니다 + NotStarted: 의도가 시작되지 않았거나 이미 종료되었습니다 + NotSucceeded: 의도가 성공하지 않았습니다 + TokenCreationFailed: 토큰 생성 실패 + InvalidToken: 의도 토큰이 유효하지 않습니다 + OtherUser: 다른 사용자를 위한 의도입니다 + AuthRequest: + AlreadyExists: 인증 요청이 이미 존재합니다 + NotExisting: 인증 요청이 존재하지 않습니다 + WrongLoginClient: 다른 로그인 클라이언트에 의해 생성된 인증 요청 + OIDCSession: + RefreshTokenInvalid: 새로 고침 토큰이 유효하지 않습니다 + Token: + Invalid: 토큰이 유효하지 않습니다 + Expired: 토큰이 만료되었습니다 + InvalidClient: 토큰이 이 클라이언트에 대해 발행되지 않았습니다 + Feature: + NotExisting: 기능이 존재하지 않습니다 + TypeNotSupported: 기능 유형이 지원되지 않습니다 + InvalidValue: 이 기능에 대해 유효하지 않은 값 + Target: + Invalid: 대상이 유효하지 않습니다 + NoTimeout: 대상에 타임아웃이 없습니다 + InvalidURL: 대상 URL이 유효하지 않습니다 + NotFound: 대상을 찾을 수 없습니다 + Execution: + ConditionInvalid: 실행 조건이 유효하지 않습니다 + Invalid: 실행이 유효하지 않습니다 + NotFound: 실행을 찾을 수 없습니다 + IncludeNotFound: 포함을 찾을 수 없습니다 + NoTargets: 정의된 대상이 없습니다 + Failed: 실행 실패 + ResponseIsNotValidJSON: 응답이 유효한 JSON이 아닙니다 + UserSchema: + NotEnabled: "\"사용자 스키마\" 기능이 활성화되지 않았습니다" + Type: + Missing: 사용자 스키마 유형이 누락되었습니다 + AlreadyExists: 사용자 스키마 유형이 이미 존재합니다 + Authenticator: + Invalid: 인증기 유형이 유효하지 않습니다 + NotActive: 사용자 스키마가 활성 상태가 아닙니다 + NotInactive: 사용자 스키마가 비활성 상태가 아닙니다 + NotExists: 사용자 스키마가 존재하지 않습니다 + ID: + Missing: 사용자 스키마 ID가 누락되었습니다 + Invalid: 사용자 스키마가 유효하지 않습니다 + Data: + Invalid: 사용자 스키마에 대한 데이터가 유효하지 않습니다 + TokenExchange: + FeatureDisabled: 토큰 교환 기능이 인스턴스에서 비활성화되어 있습니다. https://zitadel.com/docs/apis/resources/feature_service_v2/feature-service-set-instance-features + Token: + Missing: 토큰이 누락되었습니다 + Invalid: 토큰이 유효하지 않습니다 + TypeMissing: 토큰 유형이 누락되었습니다 + TypeNotAllowed: 허용되지 않는 토큰 유형입니다 + TypeNotSupported: 지원되지 않는 토큰 유형입니다 + NotForAPI: API에 대해 대리 인증된 토큰을 허용하지 않습니다 + Impersonation: + PolicyDisabled: 인스턴스 보안 정책에서 대리 인증이 비활성화되었습니다 + WebKey: + ActiveDelete: 활성 웹 키를 삭제할 수 없습니다 + Config: 웹 키 설정이 유효하지 않습니다 + Duplicate: 웹 키 ID가 고유하지 않습니다 + FeatureDisabled: 웹 키 기능이 비활성화되었습니다 + NoActive: 활성 웹 키가 없습니다 + NotFound: 웹 키를 찾을 수 없습니다 + +AggregateTypes: + action: 작업 + instance: 인스턴스 + key_pair: 키 쌍 + org: 조직 + project: 프로젝트 + user: 사용자 + usergrant: 사용자 권한 + quota: 할당량 + feature: 기능 + target: 대상 + execution: 실행 + user_schema: 사용자 스키마 + auth_request: 인증 요청 + device_auth: 디바이스 인증 + idpintent: IDP 의도 + limits: 제한 + milestone: 마일스톤 + oidc_session: OIDC 세션 + restrictions: 제한 사항 + system: 시스템 + session: 세션 + web_key: 웹 키 + +EventTypes: + execution: + set: 실행 설정됨 + removed: 실행 삭제됨 + target: + added: 대상 생성됨 + changed: 대상 변경됨 + removed: 대상 삭제됨 + user: + added: 사용자 추가됨 + selfregistered: 사용자가 자체 등록함 + initialization: + code: + added: 초기화 코드 생성됨 + sent: 초기화 코드 전송됨 + check: + succeeded: 초기화 확인 성공 + failed: 초기화 확인 실패 + token: + added: 액세스 토큰 생성됨 + v2.added: 액세스 토큰 생성됨 + removed: 액세스 토큰 삭제됨 + impersonated: 사용자 대리 로그인됨 + username: + reserved: 사용자 이름 예약됨 + released: 사용자 이름 해제됨 + changed: 사용자 이름 변경됨 + email: + reserved: 이메일 주소 예약됨 + released: 이메일 주소 해제됨 + changed: 이메일 주소 변경됨 + verified: 이메일 주소 인증됨 + verification: + failed: 이메일 주소 인증 실패 + code: + added: 이메일 인증 코드 생성됨 + sent: 이메일 인증 코드 전송됨 + machine: + added: 기술 사용자 추가됨 + changed: 기술 사용자 변경됨 + key: + added: 키 추가됨 + removed: 키 삭제됨 + secret: + set: 시크릿 설정됨 + updated: 시크릿 해시 업데이트됨 + removed: 시크릿 삭제됨 + check: + succeeded: 시크릿 확인 성공 + failed: 시크릿 확인 실패 + human: + added: 사용자 추가됨 + selfregistered: 사용자가 자체 등록함 + avatar: + added: 아바타 추가됨 + removed: 아바타 삭제됨 + initialization: + code: + added: 초기화 코드 생성됨 + sent: 초기화 코드 전송됨 + check: + succeeded: 초기화 확인 성공 + failed: 초기화 확인 실패 + invite: + code: + added: 초대 코드 생성됨 + sent: 초대 코드 전송됨 + check: + succeeded: 초대 확인 성공 + failed: 초대 확인 실패 + username: + reserved: 사용자 이름 예약됨 + released: 사용자 이름 해제됨 + email: + changed: 이메일 주소 변경됨 + verified: 이메일 주소 인증됨 + verification: + failed: 이메일 주소 인증 실패 + code: + added: 이메일 인증 코드 생성됨 + sent: 이메일 인증 코드 전송됨 + password: + changed: 비밀번호 변경됨 + code: + added: 비밀번호 코드 생성됨 + sent: 비밀번호 코드 전송됨 + check: + succeeded: 비밀번호 확인 성공 + failed: 비밀번호 확인 실패 + change: + sent: 비밀번호 변경 전송됨 + hash: + updated: 비밀번호 해시 업데이트됨 + externallogin: + check: + succeeded: 외부 로그인 성공 + externalidp: + added: 외부 IDP 추가됨 + removed: 외부 IDP 삭제됨 + cascade: + removed: 외부 IDP 연쇄 삭제됨 + id: + migrated: 외부 IDP의 사용자 ID가 마이그레이션됨 + phone: + changed: 전화번호 변경됨 + verified: 전화번호 인증됨 + verification: + failed: 전화번호 인증 실패 + code: + added: 전화번호 코드 생성됨 + sent: 전화번호 코드 전송됨 + removed: 전화번호 삭제됨 + profile: + changed: 사용자 프로필 변경됨 + address: + changed: 사용자 주소 변경됨 + mfa: + otp: + added: 다중인증 OTP 추가됨 + verified: 다중인증 OTP 인증됨 + removed: 다중인증 OTP 삭제됨 + check: + succeeded: 다중인증 OTP 확인 성공 + failed: 다중인증 OTP 확인 실패 + sms: + added: 다중인증 OTP SMS 추가됨 + removed: 다중인증 OTP SMS 삭제됨 + code: + added: 다중인증 OTP SMS 코드 추가됨 + sent: 다중인증 OTP SMS 코드 전송됨 + check: + succeeded: 다중인증 OTP SMS 확인 성공 + failed: 다중인증 OTP SMS 확인 실패 + email: + added: 다중인증 OTP 이메일 추가됨 + removed: 다중인증 OTP 이메일 삭제됨 + code: + added: 다중인증 OTP 이메일 코드 추가됨 + sent: 다중인증 OTP 이메일 코드 전송됨 + check: + succeeded: 다중인증 OTP 이메일 확인 성공 + failed: 다중인증 OTP 이메일 확인 실패 + u2f: + token: + added: 다중인증 U2F 토큰 추가됨 + verified: 다중인증 U2F 토큰 인증됨 + removed: 다중인증 U2F 토큰 삭제됨 + begin: + login: 다중인증 U2F 확인 시작됨 + check: + succeeded: 다중인증 U2F 확인 성공 + failed: 다중인증 U2F 확인 실패 + signcount: + changed: 다중인증 U2F 토큰의 체크섬이 변경됨 + init: + skipped: 다중인증 초기화 건너뜀 + passwordless: + token: + added: 비밀번호 없는 로그인 토큰 추가됨 + verified: 비밀번호 없는 로그인 토큰 인증됨 + removed: 비밀번호 없는 로그인 토큰 삭제됨 + begin: + login: 비밀번호 없는 로그인 확인 시작됨 + check: + succeeded: 비밀번호 없는 로그인 확인 성공 + failed: 비밀번호 없는 로그인 확인 실패 + signcount: + changed: 비밀번호 없는 로그인 토큰의 체크섬이 변경됨 + initialization: + code: + added: 비밀번호 없는 초기화 코드 추가됨 + sent: 비밀번호 없는 초기화 코드 전송됨 + requested: 비밀번호 없는 초기화 코드 요청됨 + check: + succeeded: 비밀번호 없는 초기화 코드 확인 성공 + failed: 비밀번호 없는 초기화 코드 확인 실패 + signed: + out: 사용자 로그아웃됨 + refresh: + token: + added: 리프레시 토큰 생성됨 + renewed: 리프레시 토큰 갱신됨 + removed: 리프레시 토큰 삭제됨 + locked: 사용자 잠금됨 + unlocked: 사용자 잠금 해제됨 + deactivated: 사용자 비활성화됨 + reactivated: 사용자 재활성화됨 + removed: 사용자 삭제됨 + password: + changed: 비밀번호 변경됨 + code: + added: 비밀번호 코드 생성됨 + sent: 비밀번호 코드 전송됨 + check: + succeeded: 비밀번호 확인 성공 + failed: 비밀번호 확인 실패 + phone: + changed: 전화번호 변경됨 + verified: 전화번호 인증됨 + verification: + failed: 전화번호 인증 실패 + code: + added: 전화번호 코드 생성됨 + sent: 전화번호 코드 전송됨 + + profile: + changed: 사용자 프로필 변경됨 + address: + changed: 사용자 주소 변경됨 + mfa: + otp: + added: 다중인증 OTP 추가됨 + verified: 다중인증 OTP 인증됨 + removed: 다중인증 OTP 삭제됨 + check: + succeeded: 다중인증 OTP 확인 성공 + failed: 다중인증 OTP 확인 실패 + init: + skipped: 다중인증 OTP 초기화 건너뜀 + init: + skipped: 다중인증 초기화 건너뜀 + signed: + out: 사용자 로그아웃됨 + grant: + added: 권한 추가됨 + changed: 권한 변경됨 + removed: 권한 삭제됨 + deactivated: 권한 비활성화됨 + reactivated: 권한 재활성화됨 + reserved: 권한 예약됨 + released: 권한 해제됨 + cascade: + removed: 권한 연쇄 삭제됨 + changed: 권한 변경됨 + metadata: + set: 사용자 메타데이터 설정됨 + removed: 사용자 메타데이터 삭제됨 + removed.all: 모든 사용자 메타데이터 삭제됨 + domain: + claimed: 도메인 클레임됨 + claimed.sent: 도메인 클레임 알림 전송됨 + pat: + added: 개인 액세스 토큰 추가됨 + removed: 개인 액세스 토큰 삭제됨 + org: + added: 조직 추가됨 + changed: 조직 변경됨 + deactivated: 조직 비활성화됨 + reactivated: 조직 재활성화됨 + removed: 조직 삭제됨 + domain: + added: 도메인 추가됨 + verification: + added: 도메인 인증 추가됨 + failed: 도메인 인증 실패 + verified: 도메인 인증됨 + removed: 도메인 삭제됨 + primary: + set: 기본 도메인 설정됨 + reserved: 도메인 예약됨 + released: 도메인 해제됨 + name: + reserved: 조직 이름 예약됨 + released: 조직 이름 해제됨 + member: + added: 조직 멤버 추가됨 + changed: 조직 멤버 변경됨 + removed: 조직 멤버 삭제됨 + cascade: + removed: 조직 멤버 연쇄 삭제됨 + iam: + policy: + added: 시스템 정책 추가됨 + changed: 시스템 정책 변경됨 + removed: 시스템 정책 삭제됨 + idp: + config: + added: IDP 설정 추가됨 + changed: IDP 설정 변경됨 + removed: IDP 설정 삭제됨 + deactivated: IDP 설정 비활성화됨 + reactivated: IDP 설정 재활성화됨 + oidc: + config: + added: OIDC IDP 설정 추가됨 + changed: OIDC IDP 설정 변경됨 + saml: + config: + added: SAML IDP 설정 추가됨 + changed: SAML IDP 설정 변경됨 + jwt: + config: + added: JWT IDP 설정 추가됨 + changed: JWT IDP 설정 변경됨 + customtext: + set: 사용자 지정 텍스트 설정됨 + removed: 사용자 지정 텍스트 삭제됨 + template: + removed: 사용자 지정 템플릿 삭제됨 + policy: + login: + added: 로그인 정책 추가됨 + changed: 로그인 정책 변경됨 + removed: 로그인 정책 삭제됨 + idpprovider: + added: 로그인 정책에 IDP 추가됨 + removed: 로그인 정책에서 IDP 삭제됨 + cascade: + removed: 로그인 정책에서 연쇄 삭제됨 + secondfactor: + added: 로그인 정책에 2차 인증 추가됨 + removed: 로그인 정책에서 2차 인증 삭제됨 + multifactor: + added: 로그인 정책에 다중인증 추가됨 + removed: 로그인 정책에서 다중인증 삭제됨 + password: + complexity: + added: 비밀번호 복잡도 정책 추가됨 + changed: 비밀번호 복잡도 정책 변경됨 + removed: 비밀번호 복잡도 정책 삭제됨 + age: + added: 비밀번호 만료 정책 추가됨 + changed: 비밀번호 만료 정책 변경됨 + removed: 비밀번호 만료 정책 삭제됨 + lockout: + added: 비밀번호 잠금 정책 추가됨 + changed: 비밀번호 잠금 정책 변경됨 + removed: 비밀번호 잠금 정책 삭제됨 + label: + added: 레이블 정책 추가됨 + changed: 레이블 정책 변경됨 + activated: 레이블 정책 활성화됨 + removed: 레이블 정책 삭제됨 + logo: + added: 레이블 정책에 로고 추가됨 + removed: 레이블 정책에서 로고 삭제됨 + dark: + added: 레이블 정책에 다크 모드 로고 추가됨 + removed: 레이블 정책에서 다크 모드 로고 삭제됨 + icon: + added: 레이블 정책에 아이콘 추가됨 + removed: 레이블 정책에서 아이콘 삭제됨 + dark: + added: 레이블 정책에 다크 모드 아이콘 추가됨 + removed: 레이블 정책에서 다크 모드 아이콘 삭제됨 + font: + added: 레이블 정책에 폰트 추가됨 + removed: 레이블 정책에서 폰트 삭제됨 + assets: + removed: 레이블 정책에서 자산 삭제됨 + privacy: + added: 개인정보처리방침 및 이용 약관 추가됨 + changed: 개인정보처리방침 및 이용 약관 변경됨 + removed: 개인정보처리방침 및 이용 약관 삭제됨 + domain: + added: 도메인 정책 추가됨 + changed: 도메인 정책 변경됨 + removed: 도메인 정책 삭제됨 + lockout: + added: 잠금 정책 추가됨 + changed: 잠금 정책 변경됨 + removed: 잠금 정책 삭제됨 + notification: + added: 알림 정책 추가됨 + changed: 알림 정책 변경됨 + removed: 알림 정책 삭제됨 + flow: + trigger_actions: + set: 작업 설정됨 + cascade: + removed: 연쇄 작업 삭제됨 + removed: 작업 삭제됨 + cleared: 플로우 초기화됨 + mail: + template: + added: 이메일 템플릿 추가됨 + changed: 이메일 템플릿 변경됨 + removed: 이메일 템플릿 삭제됨 + text: + added: 이메일 텍스트 추가됨 + changed: 이메일 텍스트 변경됨 + removed: 이메일 텍스트 삭제됨 + metadata: + removed: 메타데이터 삭제됨 + removed.all: 모든 메타데이터 삭제됨 + set: 메타데이터 설정됨 + project: + added: 프로젝트 추가됨 + changed: 프로젝트 변경됨 + deactivated: 프로젝트 비활성화됨 + reactivated: 프로젝트 재활성화됨 + removed: 프로젝트 삭제됨 + member: + added: 프로젝트 멤버 추가됨 + changed: 프로젝트 멤버 변경됨 + removed: 프로젝트 멤버 삭제됨 + cascade: + removed: 프로젝트 멤버 연쇄 삭제됨 + role: + added: 프로젝트 역할 추가됨 + changed: 프로젝트 역할 변경됨 + removed: 프로젝트 역할 삭제됨 + grant: + added: 관리 액세스 추가됨 + changed: 관리 액세스 변경됨 + removed: 관리 액세스 삭제됨 + deactivated: 관리 액세스 비활성화됨 + reactivated: 관리 액세스 재활성화됨 + cascade: + changed: 관리 액세스 변경됨 + member: + added: 관리 액세스 멤버 추가됨 + changed: 관리 액세스 멤버 변경됨 + removed: 관리 액세스 멤버 삭제됨 + cascade: + removed: 관리 액세스 연쇄 삭제됨 + application: + added: 애플리케이션 추가됨 + changed: 애플리케이션 변경됨 + removed: 애플리케이션 삭제됨 + deactivated: 애플리케이션 비활성화됨 + reactivated: 애플리케이션 재활성화됨 + oidc: + secret: + check: + succeeded: OIDC 클라이언트 시크릿 확인 성공 + failed: OIDC 클라이언트 시크릿 확인 실패 + key: + added: OIDC 앱 키 추가됨 + removed: OIDC 앱 키 삭제됨 + api: + secret: + check: + succeeded: API 시크릿 확인 성공 + failed: API 시크릿 확인 실패 + key: + added: 애플리케이션 키 추가됨 + removed: 애플리케이션 키 삭제됨 + config: + saml: + added: SAML 설정 추가됨 + changed: SAML 설정 변경됨 + oidc: + added: OIDC 설정 추가됨 + changed: OIDC 설정 변경됨 + secret: + changed: OIDC 시크릿 변경됨 + updated: OIDC 시크릿 해시 갱신됨 + api: + added: API 설정 추가됨 + changed: API 설정 변경됨 + secret: + changed: API 시크릿 변경됨 + updated: API 시크릿 해시 갱신됨 + policy: + password: + complexity: + added: 비밀번호 복잡도 정책 추가됨 + changed: 비밀번호 복잡도 정책 변경됨 + age: + added: 비밀번호 만료 정책 추가됨 + changed: 비밀번호 만료 정책 변경됨 + lockout: + added: 비밀번호 잠금 정책 추가됨 + changed: 비밀번호 잠금 정책 변경됨 + iam: + setup: + started: ZITADEL 설정 시작됨 + done: ZITADEL 설정 완료됨 + global: + org: + set: 글로벌 조직 설정됨 + project: + iam: + set: ZITADEL 프로젝트 설정됨 + member: + added: ZITADEL 멤버 추가됨 + changed: ZITADEL 멤버 변경됨 + removed: ZITADEL 멤버 삭제됨 + cascade: + removed: ZITADEL 멤버 연쇄 삭제됨 + idp: + config: + added: IDP 설정 추가됨 + changed: IDP 설정 변경됨 + removed: IDP 설정 삭제됨 + deactivated: IDP 설정 비활성화됨 + reactivated: IDP 설정 재활성화됨 + oidc: + config: + added: OIDC IDP 설정 추가됨 + changed: OIDC IDP 설정 변경됨 + saml: + config: + added: SAML IDP 설정 추가됨 + changed: SAML IDP 설정 변경됨 + jwt: + config: + added: IDP에 JWT 설정 추가됨 + changed: IDP로부터 JWT 설정 삭제됨 + customtext: + set: 텍스트 설정됨 + removed: 텍스트 삭제됨 + policy: + login: + added: 기본 로그인 정책 추가됨 + changed: 기본 로그인 정책 변경됨 + idpprovider: + added: 기본 로그인 정책에 IDP 추가됨 + removed: 기본 로그인 정책에서 IDP 삭제됨 + label: + added: 레이블 정책 추가됨 + changed: 레이블 정책 변경됨 + activated: 레이블 정책 활성화됨 + logo: + added: 로고가 레이블 정책에 추가됨 + removed: 레이블 정책에서 로고가 삭제됨 + dark: + added: 다크 모드 로고가 레이블 정책에 추가됨 + removed: 다크 모드 로고가 레이블 정책에서 삭제됨 + icon: + added: 아이콘이 레이블 정책에 추가됨 + removed: 레이블 정책에서 아이콘이 삭제됨 + dark: + added: 다크 모드 아이콘이 레이블 정책에 추가됨 + removed: 다크 모드 아이콘이 레이블 정책에서 삭제됨 + font: + added: 폰트가 레이블 정책에 추가됨 + removed: 레이블 정책에서 폰트가 삭제됨 + assets: + removed: 레이블 정책에서 자산 삭제됨 + default: + language: + set: 기본 언어 설정됨 + oidc: + settings: + added: OIDC 설정 추가됨 + changed: OIDC 설정 변경됨 + removed: OIDC 설정 삭제됨 + secret: + generator: + added: 시크릿 생성기 추가됨 + changed: 시크릿 생성기 변경됨 + removed: 시크릿 생성기 삭제됨 + smtp: + config: + added: SMTP 설정 추가됨 + changed: SMTP 설정 변경됨 + activated: SMTP 설정 활성화됨 + deactivated: SMTP 설정 비활성화됨 + removed: SMTP 설정 삭제됨 + password: + changed: SMTP 설정 비밀번호 변경됨 + sms: + config: + twilio: + added: Twilio SMS 제공자 추가됨 + changed: Twilio SMS 제공자 변경됨 + token: + changed: Twilio SMS 제공자 토큰 변경됨 + removed: Twilio SMS 제공자 삭제됨 + activated: Twilio SMS 제공자 활성화됨 + deactivated: Twilio SMS 제공자 비활성화됨 + key_pair: + added: 키 페어 추가됨 + certificate: + added: 인증서 추가됨 + action: + added: 작업 추가됨 + changed: 작업 변경됨 + deactivated: 작업 비활성화됨 + reactivated: 작업 재활성화됨 + removed: 작업 삭제됨 + instance: + added: 인스턴스 추가됨 + changed: 인스턴스 변경됨 + customtext: + removed: 사용자 정의 텍스트 삭제됨 + set: 사용자 정의 텍스트 설정됨 + template: + removed: 사용자 정의 템플릿 삭제됨 + default: + language: + set: 기본 언어 설정됨 + org: + set: 기본 조직 설정됨 + domain: + added: 도메인 추가됨 + primary: + set: 기본 도메인 설정됨 + removed: 도메인 삭제됨 + iam: + console: + set: ZITADEL 콘솔 애플리케이션 설정됨 + project: + set: ZITADEL 프로젝트 설정됨 + mail: + template: + added: 이메일 템플릿 추가됨 + changed: 이메일 템플릿 변경됨 + text: + added: 이메일 텍스트 추가됨 + changed: 이메일 텍스트 변경됨 + member: + added: 인스턴스 멤버 추가됨 + changed: 인스턴스 멤버 변경됨 + removed: 인스턴스 멤버 삭제됨 + cascade: + removed: 인스턴스 멤버 연쇄 삭제됨 + notification: + provider: + debug: + fileadded: 파일 디버그 알림 제공자 추가됨 + filechanged: 파일 디버그 알림 제공자 변경됨 + fileremoved: 파일 디버그 알림 제공자 삭제됨 + logadded: 로그 디버그 알림 제공자 추가됨 + logchanged: 로그 디버그 알림 제공자 변경됨 + logremoved: 로그 디버그 알림 제공자 삭제됨 + oidc: + settings: + added: OIDC 설정 추가됨 + changed: OIDC 설정 변경됨 + policy: + domain: + added: 도메인 정책 추가됨 + changed: 도메인 정책 변경됨 + label: + activated: 레이블 정책 활성화됨 + added: 레이블 정책 추가됨 + assets: + removed: 레이블 정책에서 자산 삭제됨 + changed: 레이블 정책 변경됨 + font: + added: 레이블 정책에 폰트 추가됨 + removed: 레이블 정책에서 폰트 삭제됨 + icon: + added: 레이블 정책에 아이콘 추가됨 + removed: 레이블 정책에서 아이콘 삭제됨 + dark: + added: 레이블 정책에 다크 모드 아이콘 추가됨 + removed: 레이블 정책에서 다크 모드 아이콘 삭제됨 + logo: + added: 레이블 정책에 로고 추가됨 + removed: 레이블 정책에서 로고 삭제됨 + dark: + added: 레이블 정책에 다크 모드 로고 추가됨 + removed: 레이블 정책에서 다크 모드 로고 삭제됨 + lockout: + added: 잠금 정책 추가됨 + changed: 잠금 정책 변경됨 + login: + added: 로그인 정책 추가됨 + changed: 로그인 정책 변경됨 + idpprovider: + added: 로그인 정책에 IDP 추가됨 + cascade: + removed: 로그인 정책에서 연쇄 IDP 삭제됨 + removed: 로그인 정책에서 IDP 삭제됨 + multifactor: + added: 다중 인증 추가됨 + removed: 다중 인증 삭제됨 + secondfactor: + added: 2차 인증 추가됨 + removed: 2차 인증 삭제됨 + password: + age: + added: 비밀번호 만료 정책 추가됨 + changed: 비밀번호 만료 정책 변경됨 + complexity: + added: 비밀번호 복잡도 정책 추가됨 + changed: 비밀번호 복잡도 정책 변경됨 + privacy: + added: 개인정보 보호 정책 추가됨 + changed: 개인정보 보호 정책 변경됨 + security: + set: 보안 정책 설정됨 + + removed: 인스턴스 삭제됨 + secret: + generator: + added: 시크릿 생성기 추가됨 + changed: 시크릿 생성기 변경됨 + removed: 시크릿 생성기 삭제됨 + sms: + configtwilio: + activated: Twilio SMS 설정 활성화됨 + added: Twilio SMS 설정 추가됨 + changed: Twilio SMS 설정 변경됨 + deactivated: Twilio SMS 설정 비활성화됨 + removed: Twilio SMS 설정 삭제됨 + token: + changed: Twilio SMS 설정 토큰 변경됨 + smtp: + config: + added: SMTP 설정 추가됨 + changed: SMTP 설정 변경됨 + activated: SMTP 설정 활성화됨 + deactivated: SMTP 설정 비활성화됨 + password: + changed: SMTP 설정 비밀번호 변경됨 + removed: SMTP 설정 삭제됨 + user_schema: + created: 사용자 스키마 생성됨 + updated: 사용자 스키마 업데이트됨 + deactivated: 사용자 스키마 비활성화됨 + reactivated: 사용자 스키마 재활성화됨 + deleted: 사용자 스키마 삭제됨 + user: + created: 사용자 생성됨 + updated: 사용자 업데이트됨 + deleted: 사용자 삭제됨 + email: + updated: 이메일 주소 변경됨 + verified: 이메일 주소 인증됨 + verification: + failed: 이메일 주소 인증 실패 + code: + added: 이메일 주소 인증 코드 생성됨 + sent: 이메일 주소 인증 코드 전송됨 + phone: + updated: 전화번호 변경됨 + verified: 전화번호 인증됨 + verification: + failed: 전화번호 인증 실패 + code: + added: 전화번호 인증 코드 생성됨 + sent: 전화번호 인증 코드 전송됨 + + + web_key: + added: 웹 키 추가됨 + activated: 웹 키 활성화됨 + deactivated: 웹 키 비활성화됨 + removed: 웹 키 삭제됨 + +Application: + OIDC: + UnsupportedVersion: 지원되지 않는 OIDC 버전입니다 + V1: + NotCompliant: 설정이 OIDC 1.0 표준과 일치하지 않습니다. + NoRedirectUris: 적어도 하나의 리디렉션 URI가 등록되어야 합니다. + NotAllCombinationsAreAllowed: 설정은 준수하지만 모든 가능한 조합이 허용되는 것은 아닙니다. + Code: + RedirectUris: + HttpOnlyForWeb: 코드 인증 방식에서는 웹 앱 유형에 대해서만 HTTP 리디렉션 URI가 허용됩니다. + CustomOnlyForNative: 코드 인증 방식에서는 네이티브 앱 유형에 대해서만 사용자 정의 리디렉션 URI를 허용합니다 (e.g appname:// ). + Implicit: + RedirectUris: + CustomNotAllowed: 암시적 인증 방식에서는 사용자 정의 리디렉션 URI를 허용하지 않습니다. + HttpNotAllowed: 암시적 인증 방식에서는 HTTP 리디렉션 URI를 허용하지 않습니다. + HttpLocalhostOnlyForNative: http://localhost 리디렉션 URI는 네이티브 애플리케이션에서만 허용됩니다. + Native: + AuthMethodType: + NotNone: 네이티브 애플리케이션의 authmethodtype은 none이어야 합니다. + RedirectUris: + MustBeHttpLocalhost: 리디렉션 URI는 http://127.0.0.1, http://[::1], http://localhost와 같은 자체 프로토콜로 시작해야 합니다. + UserAgent: + AuthMethodType: + NotNone: 사용자 에이전트 앱의 authmethodtype은 none이어야 합니다. + GrantType: + Refresh: + NoAuthCode: 리프레시 토큰은 인증 코드와 함께 사용할 때만 허용됩니다. + +Action: + Flow: + Type: + Unspecified: 미지정 + ExternalAuthentication: 외부 인증 + CustomiseToken: 토큰 커스터마이징 + InternalAuthentication: 내부 인증 + CustomizeSAMLResponse: SAML 응답 커스터마이징 + TriggerType: + Unspecified: 미지정 + PostAuthentication: 인증 후 + PreCreation: 생성 전 + PostCreation: 생성 후 + PreUserinfoCreation: 사용자 정보 생성 전 + PreAccessTokenCreation: 액세스 토큰 생성 전 + PreSAMLResponseCreation: SAML 응답 생성 전 From 36c197590f99ef080a78525303565e0919c2afd1 Mon Sep 17 00:00:00 2001 From: asoji <99072163+asoji@users.noreply.github.com> Date: Mon, 2 Dec 2024 07:51:06 -0800 Subject: [PATCH 4/7] docs(adopter): devOS: Sanity Edition org (#8986) N/A --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index ba740212b2..10e8fc28b4 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -12,6 +12,7 @@ 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 | From 26e936aec3ad49c27f7c1db86d0c0d7772c187ea Mon Sep 17 00:00:00 2001 From: Fabi Date: Mon, 2 Dec 2024 17:52:55 +0100 Subject: [PATCH 5/7] fix: miss-leading labels in the console (#8972) # Which Problems Are Solved On the login settings we do have the settings "Force MFA" and "Force MFA for local authenticated users" this gives the impression, that i can enable both and then all users should be forced to use an mfa. But when both settings are enabled, only local users are forced to add mfa. # How the Problems Are Solved The label was wrong, the second one should be "Force MFA for local authneticated users only", I changed both labels to make it easier to understand. --- console/src/assets/i18n/bg.json | 4 ++-- console/src/assets/i18n/cs.json | 4 ++-- console/src/assets/i18n/de.json | 4 ++-- console/src/assets/i18n/en.json | 4 ++-- console/src/assets/i18n/es.json | 4 ++-- console/src/assets/i18n/fr.json | 4 ++-- console/src/assets/i18n/hu.json | 4 ++-- console/src/assets/i18n/id.json | 4 ++-- console/src/assets/i18n/it.json | 4 ++-- console/src/assets/i18n/ja.json | 4 ++-- console/src/assets/i18n/mk.json | 4 ++-- console/src/assets/i18n/nl.json | 4 ++-- console/src/assets/i18n/pl.json | 4 ++-- console/src/assets/i18n/pt.json | 3 ++- console/src/assets/i18n/ru.json | 3 ++- console/src/assets/i18n/sv.json | 4 ++-- console/src/assets/i18n/zh.json | 4 ++-- 17 files changed, 34 insertions(+), 32 deletions(-) diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json index c196e230a1..9402ae5bb2 100644 --- a/console/src/assets/i18n/bg.json +++ b/console/src/assets/i18n/bg.json @@ -1722,8 +1722,8 @@ "ALLOWUSERNAMEPASSWORD_DESC": "Разрешено е конвенционалното влизане с потребителско име и парола.", "ALLOWEXTERNALIDP_DESC": "Входът е разрешен за основните доставчици на самоличност", "ALLOWREGISTER_DESC": "Ако опцията е избрана, в входа се появява допълнителна стъпка за регистрация на потребител.", - "FORCEMFA": "Сила MFA", - "FORCEMFALOCALONLY": "Принудително MFA за локални потребители", + "FORCEMFA": "Наложи MFA за всички потребители", + "FORCEMFALOCALONLY": "Наложи MFA само за локално автентифицирани потребители", "FORCEMFALOCALONLY_DESC": "Ако е избрана опцията, локалните удостоверени потребители трябва да конфигурират втори фактор за влизане.", "HIDEPASSWORDRESET_DESC": "Ако опцията е избрана, потребителят не може да нулира паролата си в процеса на влизане.", "HIDELOGINNAMESUFFIX": "Скриване на суфикса на името за влизане", diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json index 817587574f..2f34468cd2 100644 --- a/console/src/assets/i18n/cs.json +++ b/console/src/assets/i18n/cs.json @@ -1724,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", diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index b8f0e3285b..4adf55be3e 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1723,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", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index d18a7114fe..4e7d2e13d9 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1723,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", diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index 2367e12471..06532cc849 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -1724,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", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index 72423c79ec..2814abdc97 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1723,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", diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json index 064dd8ea5f..0ffa6b92b6 100644 --- a/console/src/assets/i18n/hu.json +++ b/console/src/assets/i18n/hu.json @@ -1721,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", diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json index 9dd80d7902..c12fbbe555 100644 --- a/console/src/assets/i18n/id.json +++ b/console/src/assets/i18n/id.json @@ -1587,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", diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index f69e97bea7..d21396991c 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1723,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", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 7e9f102ecf..936262d132 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1718,8 +1718,8 @@ "ALLOWUSERNAMEPASSWORD_DESC": "ユーザー名とパスワードを使用した従来のログインを許可します。", "ALLOWEXTERNALIDP_DESC": "基礎となるIDプロバイダーにログインを許可します。", "ALLOWREGISTER_DESC": "このオプションが選択されている場合、ユーザーを登録するための追加のステップがログインに表示されます。", - "FORCEMFA": "MFAを強制する", - "FORCEMFALOCALONLY": "ローカル ユーザーに MFA を強制する", + "FORCEMFA": "すべてのユーザーに MFA を強制する", + "FORCEMFALOCALONLY": "ローカル認証ユーザーのみに MFA を強制する", "FORCEMFALOCALONLY_DESC": "オプションが選択されている場合、ローカル認証されたユーザーはログインの 2 番目の要素を構成する必要があります。", "HIDEPASSWORDRESET_DESC": "このオプションが選択されている場合、ユーザーはログイン過程ででパスワードをリセットできません。", "HIDELOGINNAMESUFFIX": "ログイン名の接尾辞を非表示にする", diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json index 43b08abc9d..ab0481b6bf 100644 --- a/console/src/assets/i18n/mk.json +++ b/console/src/assets/i18n/mk.json @@ -1724,8 +1724,8 @@ "ALLOWUSERNAMEPASSWORD_DESC": "Дозволена е конвенционална најава со корисничко име и лозинка.", "ALLOWEXTERNALIDP_DESC": "Најавата е дозволена за поддржуваните IDPs", "ALLOWREGISTER_DESC": "Доколку е избрана опцијата, се прикажува дополнителен чекор за регистрирање на корисник во најавата.", - "FORCEMFA": "Задолжителна MFA", - "FORCEMFALOCALONLY": "Force MFA за локални корисници", + "FORCEMFA": "Наметнете MFA за сите корисници", + "FORCEMFALOCALONLY": "Наметнете MFA само за локално автентифицирани корисници", "FORCEMFALOCALONLY_DESC": "Ако е избрана опцијата, локалните автентицирани корисници треба да конфигурираат втор фактор за најавување.", "HIDEPASSWORDRESET_DESC": "Доколку е избрана опцијата, корисникот нема да може да ја ресетира својата лозинка во процесот на најава.", "HIDELOGINNAMESUFFIX": "Сокриј го суфиксот на корисничкото име", diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json index aa7008d57c..efc5513e68 100644 --- a/console/src/assets/i18n/nl.json +++ b/console/src/assets/i18n/nl.json @@ -1721,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", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index 0b4fd0daba..0443b89a89 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1722,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", diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 74ede27301..3bbb4e9c9b 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -1724,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", diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json index 775428ebcd..cdbb49d708 100644 --- a/console/src/assets/i18n/ru.json +++ b/console/src/assets/i18n/ru.json @@ -1794,7 +1794,8 @@ "ALLOWUSERNAMEPASSWORD_DESC": "Допускается стандартный вход с именем пользователя и паролем.", "ALLOWEXTERNALIDP_DESC": "Вход разрешён для основных поставщиков идентификационных данных.", "ALLOWREGISTER_DESC": "Если данный параметр выбран, при входе в систему появляется дополнительный шаг для регистрации пользователя.", - "FORCEMFA": "Принудительная многофакторная аутентификация (MFA)", + "FORCEMFA": "Принудительная многофакторная аутентификация для всех пользователей", + "FORCEMFALOCALONLY": "Принудительная многофакторная аутентификация только для локально аутентифицированных пользователей", "FORCEMFA_DESC": "Если данный параметр выбран, пользователи должны настроить двухфакторную аутентификацию для входа в систему.", "HIDEPASSWORDRESET": "Скрыть сброс пароля", "HIDEPASSWORDRESET_DESC": "Если данный параметр выбран, пользователь не может сбросить свой пароль в процессе входа в систему.", diff --git a/console/src/assets/i18n/sv.json b/console/src/assets/i18n/sv.json index d8be2e7f0f..93ed8ac72b 100644 --- a/console/src/assets/i18n/sv.json +++ b/console/src/assets/i18n/sv.json @@ -1727,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", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 50f108533d..4f1b1d1d46 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1722,8 +1722,8 @@ "ALLOWUSERNAMEPASSWORD_DESC": "允许使用用户名和密码进行登录。", "ALLOWEXTERNALIDP_DESC": "允许外部身份提供者进行登录", "ALLOWREGISTER_DESC": "如果选择了该选项,登录中会出现一个用于注册用户的附加步骤。", - "FORCEMFA": "强制使用 MFA", - "FORCEMFALOCALONLY": "对本地用户强制执行 MFA", + "FORCEMFA": "强制所有用户使用 MFA", + "FORCEMFALOCALONLY": "仅强制本地认证用户使用 MFA", "FORCEMFALOCALONLY_DESC": "如果选择该选项,本地经过身份验证的用户必须配置第二个登录因素。", "HIDEPASSWORDRESET_DESC": "如果选择该选项,则用户无法在登录过程中重置其密码。", "HIDELOGINNAMESUFFIX": "隐藏登录名后缀", From c07a5f4277277284d6ddd31a1b0f4c86da39c005 Mon Sep 17 00:00:00 2001 From: Stefan Benz <46600784+stebenz@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:14:04 +0100 Subject: [PATCH 6/7] fix: consistent permission check on user v2 (#8807) # Which Problems Are Solved Some user v2 API calls checked for permission only on the user itself. # How the Problems Are Solved Consistent check for permissions on user v2 API. # Additional Changes None # Additional Context Closes #7944 --------- Co-authored-by: Livio Spring --- .../grpc/user/v2/integration_test/otp_test.go | 73 ++++++++++++++++--- .../user/v2/integration_test/passkey_test.go | 19 ++++- internal/api/grpc/user/v2/otp.go | 1 - .../user/v2beta/integration_test/otp_test.go | 71 +++++++++++++++--- .../v2beta/integration_test/passkey_test.go | 19 ++++- internal/command/user_human_otp.go | 43 ++++------- internal/command/user_human_password.go | 2 +- internal/command/user_human_webauthn.go | 14 ++-- internal/command/user_v2.go | 10 +++ internal/command/user_v2_email.go | 13 +--- internal/command/user_v2_invite.go | 7 +- internal/command/user_v2_passkey.go | 3 +- internal/command/user_v2_passkey_test.go | 24 +++--- internal/command/user_v2_password.go | 7 +- internal/command/user_v2_phone.go | 12 +-- 15 files changed, 213 insertions(+), 105 deletions(-) diff --git a/internal/api/grpc/user/v2/integration_test/otp_test.go b/internal/api/grpc/user/v2/integration_test/otp_test.go index ae7c040427..01e6c07a40 100644 --- a/internal/api/grpc/user/v2/integration_test/otp_test.go +++ b/internal/api/grpc/user/v2/integration_test/otp_test.go @@ -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(), }, diff --git a/internal/api/grpc/user/v2/integration_test/passkey_test.go b/internal/api/grpc/user/v2/integration_test/passkey_test.go index 881ab17c09..055a47ec46 100644 --- a/internal/api/grpc/user/v2/integration_test/passkey_test.go +++ b/internal/api/grpc/user/v2/integration_test/passkey_test.go @@ -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{ diff --git a/internal/api/grpc/user/v2/otp.go b/internal/api/grpc/user/v2/otp.go index e2fe6b794d..fd76cf2b93 100644 --- a/internal/api/grpc/user/v2/otp.go +++ b/internal/api/grpc/user/v2/otp.go @@ -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) { diff --git a/internal/api/grpc/user/v2beta/integration_test/otp_test.go b/internal/api/grpc/user/v2beta/integration_test/otp_test.go index 6d6e2eff3e..fae6c069a4 100644 --- a/internal/api/grpc/user/v2beta/integration_test/otp_test.go +++ b/internal/api/grpc/user/v2beta/integration_test/otp_test.go @@ -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(), }, diff --git a/internal/api/grpc/user/v2beta/integration_test/passkey_test.go b/internal/api/grpc/user/v2beta/integration_test/passkey_test.go index acca01885c..7bc0465956 100644 --- a/internal/api/grpc/user/v2beta/integration_test/passkey_test.go +++ b/internal/api/grpc/user/v2beta/integration_test/passkey_test.go @@ -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{ diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go index e505288cbd..97596aabd8 100644 --- a/internal/command/user_human_otp.go +++ b/internal/command/user_human_otp.go @@ -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") diff --git a/internal/command/user_human_password.go b/internal/command/user_human_password.go index 9b686f88b7..a67a4b91da 100644 --- a/internal/command/user_human_password.go +++ b/internal/command/user_human_password.go @@ -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) } } diff --git a/internal/command/user_human_webauthn.go b/internal/command/user_human_webauthn.go index 3555466359..3b8a66e0d5 100644 --- a/internal/command/user_human_webauthn.go +++ b/internal/command/user_human_webauthn.go @@ -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) diff --git a/internal/command/user_v2.go b/internal/command/user_v2.go index 032ac0b8f7..033a16eb9a 100644 --- a/internal/command/user_v2.go +++ b/internal/command/user_v2.go @@ -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 diff --git a/internal/command/user_v2_email.go b/internal/command/user_v2_email.go index cc81f7399c..1618e2cd48 100644 --- a/internal/command/user_v2_email.go +++ b/internal/command/user_v2_email.go @@ -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") diff --git a/internal/command/user_v2_invite.go b/internal/command/user_v2_invite.go index 78b46a530e..1325d2e0c9 100644 --- a/internal/command/user_v2_invite.go +++ b/internal/command/user_v2_invite.go @@ -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") diff --git a/internal/command/user_v2_passkey.go b/internal/command/user_v2_passkey.go index 897a1ab41d..a386049744 100644 --- a/internal/command/user_v2_passkey.go +++ b/internal/command/user_v2_passkey.go @@ -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) diff --git a/internal/command/user_v2_passkey_test.go b/internal/command/user_v2_passkey_test.go index a6ba470d2b..0d1009862c 100644 --- a/internal/command/user_v2_passkey_test.go +++ b/internal/command/user_v2_passkey_test.go @@ -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) diff --git a/internal/command/user_v2_password.go b/internal/command/user_v2_password.go index 67bee2c28f..faa1fe14a6 100644 --- a/internal/command/user_v2_password.go +++ b/internal/command/user_v2_password.go @@ -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 diff --git a/internal/command/user_v2_phone.go b/internal/command/user_v2_phone.go index 8b754b36f3..8648f9a564 100644 --- a/internal/command/user_v2_phone.go +++ b/internal/command/user_v2_phone.go @@ -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") From ffe95707769abde4ffffa7fde62fe957adf24ab1 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Tue, 3 Dec 2024 11:38:28 +0100 Subject: [PATCH 7/7] fix(saml): improve error handling (#8928) # Which Problems Are Solved There are multiple issues with the metadata and error handling of SAML: - When providing a SAML metadata for an IdP, which cannot be processed, the error will only be noticed once a user tries to use the IdP. - Parsing for metadata with any other encoding than UTF-8 fails. - Metadata containing an enclosing EntitiesDescriptor around EntityDescriptor cannot be parsed. - Metadata's `validUntil` value is always set to 48 hours, which causes issues on external providers, if processed from a manual down/upload. - If a SAML response cannot be parsed, only a generic "Authentication failed" error is returned, the cause is hidden to the user and also to actions. # How the Problems Are Solved - Return parsing errors after create / update and retrieval of an IdP in the API. - Prevent the creation and update of an IdP in case of a parsing failure. - Added decoders for encodings other than UTF-8 (including ASCII, windows and ISO, [currently supported](https://github.com/golang/text/blob/efd25daf282ae4d20d3625f1ccb4452fe40967ae/encoding/ianaindex/ianaindex.go#L156)) - Updated parsing to handle both `EntitiesDescriptor` and `EntityDescriptor` as root element - `validUntil` will automatically set to the certificate's expiration time - Unwrapped the hidden error to be returned. The Login UI will still only provide a mostly generic error, but action can now access the underlying error. # Additional Changes None # Additional Context reported by a customer --- internal/api/grpc/admin/idp_converter.go | 8 +- internal/api/grpc/management/idp_converter.go | 8 +- internal/command/instance_idp.go | 17 +- internal/command/instance_idp_test.go | 151 +++++++++++++++--- internal/command/org_idp.go | 17 +- internal/command/org_idp_test.go | 86 +++++++--- internal/idp/providers/saml/saml.go | 44 ++++- internal/idp/providers/saml/saml_test.go | 110 +++++++++++++ internal/idp/providers/saml/session.go | 3 +- internal/idp/providers/saml/session_test.go | 2 +- 10 files changed, 377 insertions(+), 69 deletions(-) diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index 2914f94b30..67e40a44ab 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -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(), diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index 48d5a85a99..ef3914cc96 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -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(), diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index c3940c007a..99ab506424 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -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 { diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index c6181af9f1..22defda532 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -22,6 +22,73 @@ import ( "github.com/zitadel/zitadel/internal/zerrors" ) +var ( + validSAMLMetadata = []byte(` + + + + + + + + + + + + Tyw4csdpNNq0E7wi5FXWdVNkdPNg+cM6kK21VB2+iF0= + + + hWQSYmnBJENy/okk2qRDuHaZiyqpDsdV6BF9/T/LNjUh/8z4dV2NEZvkNhFEyQ+bqdj+NmRWvKqpg1dtgNJxQc32+IsLQvXNYyhMCtyG570/jaTOtm8daV4NKJyTV7SdwM6yfXgubz5YCRTyV13W2gBIFYppIRImIv5NDcjz+lEmWhnrkw8G2wRSFUY7VvkDn9rgsTzw/Pnsw6hlzpjGDYPMPx3ux3kjFVevdhFGNo+VC7t9ozruuGyH3yue9Re6FZoqa4oyWaPSOwei0ZH6UNqkX93Eo5Y49QKwaO8Rm+kWsOhdTqebVmCc+SpWbbrZbQj4nSLgWGlvCkZSivmH7ezr4Ol1ZkRetQ92UQ7xJS7E0y6uXAGvdgpDnyqHCOFfhTS6yqltHtc3m7JZex327xkv6e69uAEOSiv++sifVUIE0h/5u3hZLvwmTPrkoRVY4wgZ4ieb86QPvhw4UPeYapOhCBk5RfjoEFIeYwPUw5rtOlpTyeBJiKMpH1+mDAoa+8HQytZoMrnnY1s612vINtY7jU5igMwIk6MitQpRGibnBVBHRc2A6aE+XS333ganFK9hX6TzNkpHUb66NINDZ8Rgb1thn3MABArGlomtM5/enrAixWExZp70TSElor7SBdBW57H7OZCYUCobZuPRDLsCO6LLKeVrbdygWeRqr/o= + + + 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== + + + + + + + urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic + + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + http://localhost:8080/saml/v2/metadata IDP signing + + 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== + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:2.0:profiles:attribute:basic + + + + + + + + + http://localhost:8080/saml/v2/metadata IDP signing + + 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== + + + + +`) +) + 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), diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 597783c78f..26690b3a66 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -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 { diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index b2b4632cf6..75321bb603 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -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), diff --git a/internal/idp/providers/saml/saml.go b/internal/idp/providers/saml/saml.go index 702e1481e3..e0391bc099 100644 --- a/internal/idp/providers/saml/saml.go +++ b/internal/idp/providers/saml/saml.go @@ -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 but have " { + // 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 } diff --git a/internal/idp/providers/saml/saml_test.go b/internal/idp/providers/saml/saml_test.go index dc8e8e2115..801ddd36fc 100644 --- a/internal/idp/providers/saml/saml_test.go +++ b/internal/idp/providers/saml/saml_test.go @@ -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(``), + }, + nil, + xml.UnmarshalError("expected element type but have "), + }, + { + "valid entity descriptor", + args{ + metadata: []byte(``), + }, + &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(``), + }, + &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(``), + }, + nil, + zerrors.ThrowInternal(nil, "SAML-Ejoi3r2", "no entity found with IDPSSODescriptor"), + }, + { + "valid entities descriptor", + args{ + metadata: []byte(``), + }, + &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) + }) + } +} diff --git a/internal/idp/providers/saml/session.go b/internal/idp/providers/saml/session.go index 09de7163bb..49a04e49cb 100644 --- a/internal/idp/providers/saml/session.go +++ b/internal/idp/providers/saml/session.go @@ -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") } diff --git a/internal/idp/providers/saml/session_test.go b/internal/idp/providers/saml/session_test.go index 5ab8c7eaec..ea3e510d60 100644 --- a/internal/idp/providers/saml/session_test.go +++ b/internal/idp/providers/saml/session_test.go @@ -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"), }, }, {