feat: ldap skip tls verify (#5637)

* feat(ldap): add InsecureSkipVerify field and tlsConfig helper

Extract the inline TLS config at both LDAPS dial sites (FetchVlessFlags,
AuthenticateUser) into a tlsConfig(cfg) helper, and add a new
Config.InsecureSkipVerify bool that flows through to
tls.Config.InsecureSkipVerify. This unblocks enterprise environments
(e.g. Microsoft AD CS with internal CAs) where the server certificate
chain cannot be imported into the system trust store.

Behavior is identical when InsecureSkipVerify is false (the default) -
pure refactor + plumbing. The helper is unit-testable without a live
server, which is why it is extracted.

Closes https://github.com/MHSanaei/3x-ui/issues/5538

* feat(settings): add LdapInsecureSkipVerify setting

Plumb the new LDAP skip-TLS-verify toggle through the settings stack:
- AllSetting struct field (json/form tag: ldapInsecureSkipVerify)
- defaultValueMap default ("false")
- GetLdapInsecureSkipVerify() getter
- ldap_sync_job wiring into ldaputil.Config (FetchVlessFlags path)
- panel/user.go wiring into ldaputil.Config (AuthenticateUser path;
  the original issue's file list missed this)

Persistence is handled by UpdateAllSetting's reflect loop, matching
the existing pattern used by ldapUseTLS (no explicit setter).

Closes https://github.com/MHSanaei/3x-ui/issues/5538

* feat(ui): add Skip TLS verification switch in LDAP settings

Wire the new ldapInsecureSkipVerify setting into the hand-written
frontend model and Zod schema, and render it as a new Switch in
GeneralTab right under "Use TLS (LDAPS)". The switch is disabled
when TLS is off (the setting is meaningless without LDAPS) and shows
an insecure-warning description to make the security implication
visible to operators.

Also adds a Vitest round-trip test pinning schema acceptance and
model default-to-false behavior.

Closes https://github.com/MHSanaei/3x-ui/issues/5538

* chore(i18n): add Skip TLS verification strings to all locales

Add pages.settings.ldap.skipTlsVerify and skipTlsVerifyDesc to all 13
backend-served translation files, matching the existing repo
convention of keeping LDAP keys present in every locale (en-US, fa-IR,
ru-RU, zh-CN, zh-TW, pt-BR, ar-EG, uk-UA, id-ID, tr-TR, vi-VN, ja-JP,
es-ES). No translation-parity test exists in CI, but every other
LDAP key is replicated across all files, so this keeps the
invariant intact.

Closes https://github.com/MHSanaei/3x-ui/issues/5538

* chore(codegen): regenerate frontend artifacts

Regenerate frontend/src/generated/{zod,types,schemas,examples}.ts
and frontend/public/openapi.json via `npm run gen` to reflect the
new ldapInsecureSkipVerify field. The codegen CI job runs
`git diff --exit-code` on these files; failing to commit them would
break the build.

Closes https://github.com/MHSanaei/3x-ui/issues/5538
This commit is contained in:
Nikan Zeyaei
2026-06-28 19:40:38 +03:30
committed by GitHub
parent aef35ee0de
commit 60c54827aa
28 changed files with 180 additions and 67 deletions
+8
View File
@@ -84,6 +84,9 @@
"ldapInboundTags": {
"type": "string"
},
"ldapInsecureSkipVerify": {
"type": "boolean"
},
"ldapInvertFlag": {
"type": "boolean"
},
@@ -429,6 +432,7 @@
"ldapFlagField",
"ldapHost",
"ldapInboundTags",
"ldapInsecureSkipVerify",
"ldapInvertFlag",
"ldapPassword",
"ldapPort",
@@ -589,6 +593,9 @@
"ldapInboundTags": {
"type": "string"
},
"ldapInsecureSkipVerify": {
"type": "boolean"
},
"ldapInvertFlag": {
"type": "boolean"
},
@@ -941,6 +948,7 @@
"ldapFlagField",
"ldapHost",
"ldapInboundTags",
"ldapInsecureSkipVerify",
"ldapInvertFlag",
"ldapPassword",
"ldapPort",
+2
View File
@@ -16,6 +16,7 @@ export const EXAMPLES: Record<string, unknown> = {
"ldapFlagField": "",
"ldapHost": "",
"ldapInboundTags": "",
"ldapInsecureSkipVerify": false,
"ldapInvertFlag": false,
"ldapPassword": "",
"ldapPort": 0,
@@ -118,6 +119,7 @@ export const EXAMPLES: Record<string, unknown> = {
"ldapFlagField": "",
"ldapHost": "",
"ldapInboundTags": "",
"ldapInsecureSkipVerify": false,
"ldapInvertFlag": false,
"ldapPassword": "",
"ldapPort": 0,
+8
View File
@@ -58,6 +58,9 @@ export const SCHEMAS: Record<string, unknown> = {
"ldapInboundTags": {
"type": "string"
},
"ldapInsecureSkipVerify": {
"type": "boolean"
},
"ldapInvertFlag": {
"type": "boolean"
},
@@ -403,6 +406,7 @@ export const SCHEMAS: Record<string, unknown> = {
"ldapFlagField",
"ldapHost",
"ldapInboundTags",
"ldapInsecureSkipVerify",
"ldapInvertFlag",
"ldapPassword",
"ldapPort",
@@ -563,6 +567,9 @@ export const SCHEMAS: Record<string, unknown> = {
"ldapInboundTags": {
"type": "string"
},
"ldapInsecureSkipVerify": {
"type": "boolean"
},
"ldapInvertFlag": {
"type": "boolean"
},
@@ -915,6 +922,7 @@ export const SCHEMAS: Record<string, unknown> = {
"ldapFlagField",
"ldapHost",
"ldapInboundTags",
"ldapInsecureSkipVerify",
"ldapInvertFlag",
"ldapPassword",
"ldapPort",
+2
View File
@@ -22,6 +22,7 @@ export interface AllSetting {
ldapFlagField: string;
ldapHost: string;
ldapInboundTags: string;
ldapInsecureSkipVerify: boolean;
ldapInvertFlag: boolean;
ldapPassword: string;
ldapPort: number;
@@ -125,6 +126,7 @@ export interface AllSettingView {
ldapFlagField: string;
ldapHost: string;
ldapInboundTags: string;
ldapInsecureSkipVerify: boolean;
ldapInvertFlag: boolean;
ldapPassword: string;
ldapPort: number;
+2
View File
@@ -34,6 +34,7 @@ export const AllSettingSchema = z.object({
ldapFlagField: z.string(),
ldapHost: z.string(),
ldapInboundTags: z.string(),
ldapInsecureSkipVerify: z.boolean(),
ldapInvertFlag: z.boolean(),
ldapPassword: z.string(),
ldapPort: z.number().int().min(0).max(65535),
@@ -138,6 +139,7 @@ export const AllSettingViewSchema = z.object({
ldapFlagField: z.string(),
ldapHost: z.string(),
ldapInboundTags: z.string(),
ldapInsecureSkipVerify: z.boolean(),
ldapInvertFlag: z.boolean(),
ldapPassword: z.string(),
ldapPort: z.number().int().min(0).max(65535),
+1
View File
@@ -68,6 +68,7 @@ export class AllSetting {
ldapHost = '';
ldapPort = 389;
ldapUseTLS = false;
ldapInsecureSkipVerify = false;
ldapBindDN = '';
ldapPassword = '';
ldapBaseDN = '';
@@ -312,6 +312,17 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp
<SettingListItem paddings="small" title={t('pages.settings.ldap.useTls')}>
<Switch checked={allSetting.ldapUseTLS} onChange={(v) => updateSetting({ ldapUseTLS: v })} />
</SettingListItem>
<SettingListItem
paddings="small"
title={t('pages.settings.ldap.skipTlsVerify')}
description={t('pages.settings.ldap.skipTlsVerifyDesc')}
>
<Switch
checked={allSetting.ldapInsecureSkipVerify}
disabled={!allSetting.ldapUseTLS}
onChange={(v) => updateSetting({ ldapInsecureSkipVerify: v })}
/>
</SettingListItem>
<SettingListItem paddings="small" title={t('pages.settings.ldap.bindDn')}>
<Input value={allSetting.ldapBindDN} onChange={(e) => updateSetting({ ldapBindDN: e.target.value })} />
</SettingListItem>
+1
View File
@@ -69,6 +69,7 @@ export const AllSettingSchema = z.object({
ldapHost: z.string().optional(),
ldapPort: port.optional(),
ldapUseTLS: z.boolean().optional(),
ldapInsecureSkipVerify: z.boolean().optional(),
ldapBindDN: z.string().optional(),
ldapPassword: z.string().optional(),
ldapBaseDN: z.string().optional(),
@@ -0,0 +1,20 @@
import { describe, it, expect } from 'vitest';
import { AllSettingSchema } from '@/schemas/setting';
import { AllSetting } from '@/models/setting';
describe('ldapInsecureSkipVerify', () => {
it('parses through the Zod schema', () => {
const r = AllSettingSchema.safeParse({ ldapInsecureSkipVerify: true });
expect(r.success).toBe(true);
expect(r.success && r.data.ldapInsecureSkipVerify).toBe(true);
});
it('rejects non-boolean values', () => {
expect(AllSettingSchema.safeParse({ ldapInsecureSkipVerify: 'yes' }).success).toBe(false);
});
it('defaults to false on the model and clones from payload', () => {
expect(new AllSetting().ldapInsecureSkipVerify).toBe(false);
expect(new AllSetting({ ldapInsecureSkipVerify: true }).ldapInsecureSkipVerify).toBe(true);
});
});
+18 -17
View File
@@ -9,17 +9,22 @@ import (
)
type Config struct {
Host string
Port int
UseTLS bool
BindDN string
Password string
BaseDN string
UserFilter string
UserAttr string
FlagField string
TruthyVals []string
Invert bool
Host string
Port int
UseTLS bool
InsecureSkipVerify bool
BindDN string
Password string
BaseDN string
UserFilter string
UserAttr string
FlagField string
TruthyVals []string
Invert bool
}
func tlsConfig(cfg Config) *tls.Config {
return &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}
}
// FetchVlessFlags returns map[email]enabled
@@ -35,9 +40,7 @@ func FetchVlessFlags(cfg Config) (map[string]bool, error) {
var opts []ldap.DialOpt
if cfg.UseTLS {
opts = append(opts, ldap.DialWithTLSConfig(&tls.Config{
InsecureSkipVerify: false,
}))
opts = append(opts, ldap.DialWithTLSConfig(tlsConfig(cfg)))
}
conn, err := ldap.DialURL(ldapURL, opts...)
@@ -105,9 +108,7 @@ func AuthenticateUser(cfg Config, username, password string) (bool, error) {
var opts []ldap.DialOpt
if cfg.UseTLS {
opts = append(opts, ldap.DialWithTLSConfig(&tls.Config{
InsecureSkipVerify: false,
}))
opts = append(opts, ldap.DialWithTLSConfig(tlsConfig(cfg)))
}
conn, err := ldap.DialURL(ldapURL, opts...)
+22
View File
@@ -0,0 +1,22 @@
package ldaputil
import "testing"
func TestTLSConfig_InsecureSkipVerifyPropagates(t *testing.T) {
cases := []struct {
name string
skip bool
want bool
}{
{"default verifies", false, false},
{"skip flows through", true, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := tlsConfig(Config{InsecureSkipVerify: c.skip})
if got.InsecureSkipVerify != c.want {
t.Fatalf("InsecureSkipVerify = %v, want %v", got.InsecureSkipVerify, c.want)
}
})
}
}
+12 -11
View File
@@ -105,17 +105,18 @@ type AllSetting struct {
SubHideSettings bool `json:"subHideSettings" form:"subHideSettings"` // Hide server settings in happ subscription (Only for Happ)
// LDAP settings
LdapEnable bool `json:"ldapEnable" form:"ldapEnable"`
LdapHost string `json:"ldapHost" form:"ldapHost"`
LdapPort int `json:"ldapPort" form:"ldapPort" validate:"gte=0,lte=65535"`
LdapUseTLS bool `json:"ldapUseTLS" form:"ldapUseTLS"`
LdapBindDN string `json:"ldapBindDN" form:"ldapBindDN"`
LdapPassword string `json:"ldapPassword" form:"ldapPassword"`
LdapBaseDN string `json:"ldapBaseDN" form:"ldapBaseDN"`
LdapUserFilter string `json:"ldapUserFilter" form:"ldapUserFilter"`
LdapUserAttr string `json:"ldapUserAttr" form:"ldapUserAttr"` // e.g., mail or uid
LdapVlessField string `json:"ldapVlessField" form:"ldapVlessField"`
LdapSyncCron string `json:"ldapSyncCron" form:"ldapSyncCron"`
LdapEnable bool `json:"ldapEnable" form:"ldapEnable"`
LdapHost string `json:"ldapHost" form:"ldapHost"`
LdapPort int `json:"ldapPort" form:"ldapPort" validate:"gte=0,lte=65535"`
LdapUseTLS bool `json:"ldapUseTLS" form:"ldapUseTLS"`
LdapInsecureSkipVerify bool `json:"ldapInsecureSkipVerify" form:"ldapInsecureSkipVerify"`
LdapBindDN string `json:"ldapBindDN" form:"ldapBindDN"`
LdapPassword string `json:"ldapPassword" form:"ldapPassword"`
LdapBaseDN string `json:"ldapBaseDN" form:"ldapBaseDN"`
LdapUserFilter string `json:"ldapUserFilter" form:"ldapUserFilter"`
LdapUserAttr string `json:"ldapUserAttr" form:"ldapUserAttr"` // e.g., mail or uid
LdapVlessField string `json:"ldapVlessField" form:"ldapVlessField"`
LdapSyncCron string `json:"ldapSyncCron" form:"ldapSyncCron"`
// Generic flag configuration
LdapFlagField string `json:"ldapFlagField" form:"ldapFlagField"`
LdapTruthyValues string `json:"ldapTruthyValues" form:"ldapTruthyValues"`
+12 -11
View File
@@ -69,17 +69,18 @@ func (j *LdapSyncJob) Run() {
// --- LDAP fetch ---
cfg := ldaputil.Config{
Host: mustGetString(j.settingService.GetLdapHost),
Port: mustGetInt(j.settingService.GetLdapPort),
UseTLS: mustGetBool(j.settingService.GetLdapUseTLS),
BindDN: mustGetString(j.settingService.GetLdapBindDN),
Password: mustGetString(j.settingService.GetLdapPassword),
BaseDN: mustGetString(j.settingService.GetLdapBaseDN),
UserFilter: mustGetString(j.settingService.GetLdapUserFilter),
UserAttr: mustGetString(j.settingService.GetLdapUserAttr),
FlagField: mustGetStringOr(j.settingService.GetLdapFlagField, mustGetString(j.settingService.GetLdapVlessField)),
TruthyVals: splitCsv(mustGetString(j.settingService.GetLdapTruthyValues)),
Invert: mustGetBool(j.settingService.GetLdapInvertFlag),
Host: mustGetString(j.settingService.GetLdapHost),
Port: mustGetInt(j.settingService.GetLdapPort),
UseTLS: mustGetBool(j.settingService.GetLdapUseTLS),
InsecureSkipVerify: mustGetBool(j.settingService.GetLdapInsecureSkipVerify),
BindDN: mustGetString(j.settingService.GetLdapBindDN),
Password: mustGetString(j.settingService.GetLdapPassword),
BaseDN: mustGetString(j.settingService.GetLdapBaseDN),
UserFilter: mustGetString(j.settingService.GetLdapUserFilter),
UserAttr: mustGetString(j.settingService.GetLdapUserAttr),
FlagField: mustGetStringOr(j.settingService.GetLdapFlagField, mustGetString(j.settingService.GetLdapVlessField)),
TruthyVals: splitCsv(mustGetString(j.settingService.GetLdapTruthyValues)),
Invert: mustGetBool(j.settingService.GetLdapInvertFlag),
}
flags, err := ldaputil.FetchVlessFlags(cfg)
+10 -8
View File
@@ -60,6 +60,7 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode
host, _ := s.settingService.GetLdapHost()
port, _ := s.settingService.GetLdapPort()
useTLS, _ := s.settingService.GetLdapUseTLS()
skipVerify, _ := s.settingService.GetLdapInsecureSkipVerify()
bindDN, _ := s.settingService.GetLdapBindDN()
ldapPass, _ := s.settingService.GetLdapPassword()
baseDN, _ := s.settingService.GetLdapBaseDN()
@@ -67,14 +68,15 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode
userAttr, _ := s.settingService.GetLdapUserAttr()
cfg := ldaputil.Config{
Host: host,
Port: port,
UseTLS: useTLS,
BindDN: bindDN,
Password: ldapPass,
BaseDN: baseDN,
UserFilter: userFilter,
UserAttr: userAttr,
Host: host,
Port: port,
UseTLS: useTLS,
InsecureSkipVerify: skipVerify,
BindDN: bindDN,
Password: ldapPass,
BaseDN: baseDN,
UserFilter: userFilter,
UserAttr: userAttr,
}
ok, err := ldaputil.AuthenticateUser(cfg, username, password)
if err != nil || !ok {
+25 -20
View File
@@ -114,26 +114,27 @@ var defaultValueMap = map[string]string{
"devChannelEnable": "false",
// LDAP defaults
"ldapEnable": "false",
"ldapHost": "",
"ldapPort": "389",
"ldapUseTLS": "false",
"ldapBindDN": "",
"ldapPassword": "",
"ldapBaseDN": "",
"ldapUserFilter": "(objectClass=person)",
"ldapUserAttr": "mail",
"ldapVlessField": "vless_enabled",
"ldapSyncCron": "@every 1m",
"ldapFlagField": "",
"ldapTruthyValues": "true,1,yes,on",
"ldapInvertFlag": "false",
"ldapInboundTags": "",
"ldapAutoCreate": "false",
"ldapAutoDelete": "false",
"ldapDefaultTotalGB": "0",
"ldapDefaultExpiryDays": "0",
"ldapDefaultLimitIP": "0",
"ldapEnable": "false",
"ldapHost": "",
"ldapPort": "389",
"ldapUseTLS": "false",
"ldapInsecureSkipVerify": "false",
"ldapBindDN": "",
"ldapPassword": "",
"ldapBaseDN": "",
"ldapUserFilter": "(objectClass=person)",
"ldapUserAttr": "mail",
"ldapVlessField": "vless_enabled",
"ldapSyncCron": "@every 1m",
"ldapFlagField": "",
"ldapTruthyValues": "true,1,yes,on",
"ldapInvertFlag": "false",
"ldapInboundTags": "",
"ldapAutoCreate": "false",
"ldapAutoDelete": "false",
"ldapDefaultTotalGB": "0",
"ldapDefaultExpiryDays": "0",
"ldapDefaultLimitIP": "0",
// Event bus — per-subscriber event filtering (empty = all disabled)
"tgEnabledEvents": "login.attempt,cpu.high",
@@ -922,6 +923,10 @@ func (s *SettingService) GetLdapUseTLS() (bool, error) {
return s.getBool("ldapUseTLS")
}
func (s *SettingService) GetLdapInsecureSkipVerify() (bool, error) {
return s.getBool("ldapInsecureSkipVerify")
}
func (s *SettingService) GetLdapBindDN() (string, error) {
return s.getString("ldapBindDN")
}
+2
View File
@@ -1220,6 +1220,8 @@
"host": "مضيف LDAP",
"port": "منفذ LDAP",
"useTls": "استخدام TLS (LDAPS)",
"skipTlsVerify": "تخطي التحقق من شهادة TLS",
"skipTlsVerifyDesc": "غير آمن — يعطل التحقق من شهادة الخادم. استخدم فقط مع CA الداخلية/غير الموثوقة.",
"bindDn": "Bind DN",
"passwordConfigured": "مهيأة؛ اترك فارغاً للاحتفاظ بكلمة المرور الحالية.",
"passwordUnconfigured": "غير مهيأة.",
+2
View File
@@ -1341,6 +1341,8 @@
"host": "LDAP host",
"port": "LDAP port",
"useTls": "Use TLS (LDAPS)",
"skipTlsVerify": "Skip TLS certificate verification",
"skipTlsVerifyDesc": "Insecure — disables server certificate validation. Use only with internal/untrusted CAs.",
"bindDn": "Bind DN",
"passwordConfigured": "Configured; leave blank to keep current password.",
"passwordUnconfigured": "Not configured.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "Host LDAP",
"port": "Puerto LDAP",
"useTls": "Usar TLS (LDAPS)",
"skipTlsVerify": "Omitir verificación de certificado TLS",
"skipTlsVerifyDesc": "Inseguro — desactiva la validación del certificado del servidor. Usar solo con CA internos/no confiables.",
"bindDn": "Bind DN",
"passwordConfigured": "Configurada; deja en blanco para mantener la contraseña actual.",
"passwordUnconfigured": "No configurada.",
+2
View File
@@ -1222,6 +1222,8 @@
"host": "میزبان LDAP",
"port": "پورت LDAP",
"useTls": "استفاده از TLS (LDAPS)",
"skipTlsVerify": "رد کردن تأیید گواهی TLS",
"skipTlsVerifyDesc": "ناامن — اعتبارسنجی گواهی سرور را غیرفعال می‌کند. فقط برای CAهای داخلی/غیرمعتبر استفاده کنید.",
"bindDn": "Bind DN",
"passwordConfigured": "تنظیم‌شده؛ برای حفظ رمز فعلی خالی بگذارید.",
"passwordUnconfigured": "تنظیم نشده.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "Port LDAP",
"useTls": "Gunakan TLS (LDAPS)",
"skipTlsVerify": "Lewati verifikasi sertifikat TLS",
"skipTlsVerifyDesc": "Tidak aman — menonaktifkan validasi sertifikat server. Gunakan hanya dengan CA internal/tidak terpercaya.",
"bindDn": "Bind DN",
"passwordConfigured": "Terkonfigurasi; biarkan kosong untuk mempertahankan kata sandi saat ini.",
"passwordUnconfigured": "Tidak terkonfigurasi.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "LDAP ポート",
"useTls": "TLS (LDAPS) を使用",
"skipTlsVerify": "TLS 証明書の検証をスキップ",
"skipTlsVerifyDesc": "安全ではありません — サーバー証明書の検証を無効化します。内部/信頼できない CA でのみ使用してください。",
"bindDn": "Bind DN",
"passwordConfigured": "設定済み;現在のパスワードを保持するには空のままにします。",
"passwordUnconfigured": "未設定。",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "Host LDAP",
"port": "Porta LDAP",
"useTls": "Usar TLS (LDAPS)",
"skipTlsVerify": "Pular verificação de certificado TLS",
"skipTlsVerifyDesc": "Inseguro — desativa a validação do certificado do servidor. Use apenas com CAs internos/não confiáveis.",
"bindDn": "Bind DN",
"passwordConfigured": "Configurada; deixe em branco para manter a senha atual.",
"passwordUnconfigured": "Não configurada.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP-хост",
"port": "Порт LDAP",
"useTls": "Использовать TLS (LDAPS)",
"skipTlsVerify": "Пропустить проверку сертификата TLS",
"skipTlsVerifyDesc": "Небезопасно — отключает проверку сертификата сервера. Используйте только с внутренними/недоверенными CA.",
"bindDn": "Bind DN",
"passwordConfigured": "Настроено; оставьте пустым, чтобы сохранить текущий пароль.",
"passwordUnconfigured": "Не настроено.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "LDAP port",
"useTls": "TLS kullan (LDAPS)",
"skipTlsVerify": "TLS sertifika doğrulamasını atla",
"skipTlsVerifyDesc": "Güvenli değil — sunucu sertifika doğrulamasını devre dışı bırakır. Yalnızca dahili/güvenilmeyen CA'larla kullanın.",
"bindDn": "Bind DN",
"passwordConfigured": "Yapılandırıldı; mevcut parolayı korumak için boş bırakın.",
"passwordUnconfigured": "Yapılandırılmadı.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP-хост",
"port": "Порт LDAP",
"useTls": "Використовувати TLS (LDAPS)",
"skipTlsVerify": "Пропустити перевірку сертифіката TLS",
"skipTlsVerifyDesc": "Небезпечно — вимикає перевірку сертифіката сервера. Використовуйте лише з внутрішніми/ненадійними CA.",
"bindDn": "Bind DN",
"passwordConfigured": "Налаштовано; залиште порожнім для збереження поточного паролю.",
"passwordUnconfigured": "Не налаштовано.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "Cổng LDAP",
"useTls": "Dùng TLS (LDAPS)",
"skipTlsVerify": "Bỏ qua xác minh chứng chỉ TLS",
"skipTlsVerifyDesc": "Không an toàn — tắt xác thực chứng chỉ máy chủ. Chỉ dùng với CA nội bộ/không đáng tin.",
"bindDn": "Bind DN",
"passwordConfigured": "Đã cấu hình; để trống để giữ mật khẩu hiện tại.",
"passwordUnconfigured": "Chưa cấu hình.",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "LDAP 端口",
"useTls": "使用 TLS (LDAPS)",
"skipTlsVerify": "跳过 TLS 证书验证",
"skipTlsVerifyDesc": "不安全 — 禁用服务器证书验证。仅用于内部/不受信任的 CA。",
"bindDn": "Bind DN",
"passwordConfigured": "已配置;留空以保留当前密码。",
"passwordUnconfigured": "未配置。",
+2
View File
@@ -1220,6 +1220,8 @@
"host": "LDAP host",
"port": "LDAP 連接埠",
"useTls": "使用 TLS (LDAPS)",
"skipTlsVerify": "略過 TLS 憑證驗證",
"skipTlsVerifyDesc": "不安全 — 停用伺服器憑證驗證。僅用於內部/不受信任的 CA。",
"bindDn": "Bind DN",
"passwordConfigured": "已設定;留空以保留目前密碼。",
"passwordUnconfigured": "未設定。",