mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-05 12:24:20 +00:00
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:
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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...)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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"`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "مضيف LDAP",
|
||||
"port": "منفذ LDAP",
|
||||
"useTls": "استخدام TLS (LDAPS)",
|
||||
"skipTlsVerify": "تخطي التحقق من شهادة TLS",
|
||||
"skipTlsVerifyDesc": "غير آمن — يعطل التحقق من شهادة الخادم. استخدم فقط مع CA الداخلية/غير الموثوقة.",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "مهيأة؛ اترك فارغاً للاحتفاظ بكلمة المرور الحالية.",
|
||||
"passwordUnconfigured": "غير مهيأة.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -1222,6 +1222,8 @@
|
||||
"host": "میزبان LDAP",
|
||||
"port": "پورت LDAP",
|
||||
"useTls": "استفاده از TLS (LDAPS)",
|
||||
"skipTlsVerify": "رد کردن تأیید گواهی TLS",
|
||||
"skipTlsVerifyDesc": "ناامن — اعتبارسنجی گواهی سرور را غیرفعال میکند. فقط برای CAهای داخلی/غیرمعتبر استفاده کنید.",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "تنظیمشده؛ برای حفظ رمز فعلی خالی بگذارید.",
|
||||
"passwordUnconfigured": "تنظیم نشده.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "LDAP host",
|
||||
"port": "LDAP ポート",
|
||||
"useTls": "TLS (LDAPS) を使用",
|
||||
"skipTlsVerify": "TLS 証明書の検証をスキップ",
|
||||
"skipTlsVerifyDesc": "安全ではありません — サーバー証明書の検証を無効化します。内部/信頼できない CA でのみ使用してください。",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "設定済み;現在のパスワードを保持するには空のままにします。",
|
||||
"passwordUnconfigured": "未設定。",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "LDAP-хост",
|
||||
"port": "Порт LDAP",
|
||||
"useTls": "Использовать TLS (LDAPS)",
|
||||
"skipTlsVerify": "Пропустить проверку сертификата TLS",
|
||||
"skipTlsVerifyDesc": "Небезопасно — отключает проверку сертификата сервера. Используйте только с внутренними/недоверенными CA.",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "Настроено; оставьте пустым, чтобы сохранить текущий пароль.",
|
||||
"passwordUnconfigured": "Не настроено.",
|
||||
|
||||
@@ -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ı.",
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "LDAP-хост",
|
||||
"port": "Порт LDAP",
|
||||
"useTls": "Використовувати TLS (LDAPS)",
|
||||
"skipTlsVerify": "Пропустити перевірку сертифіката TLS",
|
||||
"skipTlsVerifyDesc": "Небезпечно — вимикає перевірку сертифіката сервера. Використовуйте лише з внутрішніми/ненадійними CA.",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "Налаштовано; залиште порожнім для збереження поточного паролю.",
|
||||
"passwordUnconfigured": "Не налаштовано.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "LDAP host",
|
||||
"port": "LDAP 端口",
|
||||
"useTls": "使用 TLS (LDAPS)",
|
||||
"skipTlsVerify": "跳过 TLS 证书验证",
|
||||
"skipTlsVerifyDesc": "不安全 — 禁用服务器证书验证。仅用于内部/不受信任的 CA。",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "已配置;留空以保留当前密码。",
|
||||
"passwordUnconfigured": "未配置。",
|
||||
|
||||
@@ -1220,6 +1220,8 @@
|
||||
"host": "LDAP host",
|
||||
"port": "LDAP 連接埠",
|
||||
"useTls": "使用 TLS (LDAPS)",
|
||||
"skipTlsVerify": "略過 TLS 憑證驗證",
|
||||
"skipTlsVerifyDesc": "不安全 — 停用伺服器憑證驗證。僅用於內部/不受信任的 CA。",
|
||||
"bindDn": "Bind DN",
|
||||
"passwordConfigured": "已設定;留空以保留目前密碼。",
|
||||
"passwordUnconfigured": "未設定。",
|
||||
|
||||
Reference in New Issue
Block a user