mhsanaei 4 dní pred
rodič
commit
713a7328f6
5 zmenil súbory, kde vykonal 225 pridanie a 228 odobranie
  1. 118 120
      util/ldap/ldap.go
  2. 22 22
      web/entity/entity.go
  3. 12 13
      web/service/inbound.go
  4. 40 40
      web/service/setting.go
  5. 33 33
      web/service/user.go

+ 118 - 120
util/ldap/ldap.go

@@ -1,144 +1,142 @@
 package ldaputil
 
 import (
-    "crypto/tls"
-    "fmt"
+	"crypto/tls"
+	"fmt"
 
-    "github.com/go-ldap/ldap/v3"
+	"github.com/go-ldap/ldap/v3"
 )
 
 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
+	BindDN     string
+	Password   string
+	BaseDN     string
+	UserFilter string
+	UserAttr   string
+	FlagField  string
+	TruthyVals []string
+	Invert     bool
 }
 
 // FetchVlessFlags returns map[email]enabled
 func FetchVlessFlags(cfg Config) (map[string]bool, error) {
-    addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
-    var conn *ldap.Conn
-    var err error
-    if cfg.UseTLS {
-        conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false})
-    } else {
-        conn, err = ldap.Dial("tcp", addr)
-    }
-    if err != nil {
-        return nil, err
-    }
-    defer conn.Close()
+	addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
+	var conn *ldap.Conn
+	var err error
+	if cfg.UseTLS {
+		conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false})
+	} else {
+		conn, err = ldap.Dial("tcp", addr)
+	}
+	if err != nil {
+		return nil, err
+	}
+	defer conn.Close()
 
-    if cfg.BindDN != "" {
-        if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil {
-            return nil, err
-        }
-    }
+	if cfg.BindDN != "" {
+		if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil {
+			return nil, err
+		}
+	}
 
-    if cfg.UserFilter == "" {
-        cfg.UserFilter = "(objectClass=person)"
-    }
-    if cfg.UserAttr == "" {
-        cfg.UserAttr = "mail"
-    }
-    // if field not set we fallback to legacy vless_enabled
-    if cfg.FlagField == "" {
-        cfg.FlagField = "vless_enabled"
-    }
+	if cfg.UserFilter == "" {
+		cfg.UserFilter = "(objectClass=person)"
+	}
+	if cfg.UserAttr == "" {
+		cfg.UserAttr = "mail"
+	}
+	// if field not set we fallback to legacy vless_enabled
+	if cfg.FlagField == "" {
+		cfg.FlagField = "vless_enabled"
+	}
 
-    req := ldap.NewSearchRequest(
-        cfg.BaseDN,
-        ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
-        cfg.UserFilter,
-        []string{cfg.UserAttr, cfg.FlagField},
-        nil,
-    )
+	req := ldap.NewSearchRequest(
+		cfg.BaseDN,
+		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
+		cfg.UserFilter,
+		[]string{cfg.UserAttr, cfg.FlagField},
+		nil,
+	)
 
-    res, err := conn.Search(req)
-    if err != nil {
-        return nil, err
-    }
+	res, err := conn.Search(req)
+	if err != nil {
+		return nil, err
+	}
 
-    result := make(map[string]bool, len(res.Entries))
-    for _, e := range res.Entries {
-        user := e.GetAttributeValue(cfg.UserAttr)
-        if user == "" {
-            continue
-        }
-        val := e.GetAttributeValue(cfg.FlagField)
-        enabled := false
-        for _, t := range cfg.TruthyVals {
-            if val == t {
-                enabled = true
-                break
-            }
-        }
-        if cfg.Invert {
-            enabled = !enabled
-        }
-        result[user] = enabled
-    }
-    return result, nil
+	result := make(map[string]bool, len(res.Entries))
+	for _, e := range res.Entries {
+		user := e.GetAttributeValue(cfg.UserAttr)
+		if user == "" {
+			continue
+		}
+		val := e.GetAttributeValue(cfg.FlagField)
+		enabled := false
+		for _, t := range cfg.TruthyVals {
+			if val == t {
+				enabled = true
+				break
+			}
+		}
+		if cfg.Invert {
+			enabled = !enabled
+		}
+		result[user] = enabled
+	}
+	return result, nil
 }
 
 // AuthenticateUser searches user by cfg.UserAttr and attempts to bind with provided password.
 func AuthenticateUser(cfg Config, username, password string) (bool, error) {
-    addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
-    var conn *ldap.Conn
-    var err error
-    if cfg.UseTLS {
-        conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false})
-    } else {
-        conn, err = ldap.Dial("tcp", addr)
-    }
-    if err != nil {
-        return false, err
-    }
-    defer conn.Close()
+	addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
+	var conn *ldap.Conn
+	var err error
+	if cfg.UseTLS {
+		conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false})
+	} else {
+		conn, err = ldap.Dial("tcp", addr)
+	}
+	if err != nil {
+		return false, err
+	}
+	defer conn.Close()
 
-    // Optional initial bind for search
-    if cfg.BindDN != "" {
-        if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil {
-            return false, err
-        }
-    }
+	// Optional initial bind for search
+	if cfg.BindDN != "" {
+		if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil {
+			return false, err
+		}
+	}
 
-    if cfg.UserFilter == "" {
-        cfg.UserFilter = "(objectClass=person)"
-    }
-    if cfg.UserAttr == "" {
-        cfg.UserAttr = "uid"
-    }
+	if cfg.UserFilter == "" {
+		cfg.UserFilter = "(objectClass=person)"
+	}
+	if cfg.UserAttr == "" {
+		cfg.UserAttr = "uid"
+	}
 
-    // Build filter to find specific user
-    filter := fmt.Sprintf("(&%s(%s=%s))", cfg.UserFilter, cfg.UserAttr, ldap.EscapeFilter(username))
-    req := ldap.NewSearchRequest(
-        cfg.BaseDN,
-        ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 1, 0, false,
-        filter,
-        []string{"dn"},
-        nil,
-    )
-    res, err := conn.Search(req)
-    if err != nil {
-        return false, err
-    }
-    if len(res.Entries) == 0 {
-        return false, nil
-    }
-    userDN := res.Entries[0].DN
-    // Try to bind as the user
-    if err := conn.Bind(userDN, password); err != nil {
-        return false, nil
-    }
-    return true, nil
+	// Build filter to find specific user
+	filter := fmt.Sprintf("(&%s(%s=%s))", cfg.UserFilter, cfg.UserAttr, ldap.EscapeFilter(username))
+	req := ldap.NewSearchRequest(
+		cfg.BaseDN,
+		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 1, 0, false,
+		filter,
+		[]string{"dn"},
+		nil,
+	)
+	res, err := conn.Search(req)
+	if err != nil {
+		return false, err
+	}
+	if len(res.Entries) == 0 {
+		return false, nil
+	}
+	userDN := res.Entries[0].DN
+	// Try to bind as the user
+	if err := conn.Bind(userDN, password); err != nil {
+		return false, nil
+	}
+	return true, nil
 }
-
-

+ 22 - 22
web/entity/entity.go

@@ -74,30 +74,30 @@ type AllSetting struct {
 	SubJsonFragment             string `json:"subJsonFragment" form:"subJsonFragment"`                         // JSON subscription fragment configuration
 	SubJsonNoises               string `json:"subJsonNoises" form:"subJsonNoises"`                             // JSON subscription noise configuration
 	SubJsonMux                  string `json:"subJsonMux" form:"subJsonMux"`                                   // JSON subscription mux configuration
-	SubJsonRules                string `json:"subJsonRules" form:"subJsonRules"`    
-	
+	SubJsonRules                string `json:"subJsonRules" form:"subJsonRules"`
+
 	// LDAP settings
-	LdapEnable                  bool   `json:"ldapEnable" form:"ldapEnable"`
-	LdapHost                    string `json:"ldapHost" form:"ldapHost"`
-	LdapPort                    int    `json:"ldapPort" form:"ldapPort"`
-	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"`
+	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"`
 	// Generic flag configuration
-	LdapFlagField               string `json:"ldapFlagField" form:"ldapFlagField"`
-	LdapTruthyValues            string `json:"ldapTruthyValues" form:"ldapTruthyValues"`
-	LdapInvertFlag              bool   `json:"ldapInvertFlag" form:"ldapInvertFlag"`
-	LdapInboundTags             string `json:"ldapInboundTags" form:"ldapInboundTags"`
-	LdapAutoCreate              bool   `json:"ldapAutoCreate" form:"ldapAutoCreate"`
-	LdapAutoDelete              bool   `json:"ldapAutoDelete" form:"ldapAutoDelete"`
-	LdapDefaultTotalGB          int    `json:"ldapDefaultTotalGB" form:"ldapDefaultTotalGB"`
-	LdapDefaultExpiryDays       int    `json:"ldapDefaultExpiryDays" form:"ldapDefaultExpiryDays"`
-	LdapDefaultLimitIP          int    `json:"ldapDefaultLimitIP" form:"ldapDefaultLimitIP"`
+	LdapFlagField         string `json:"ldapFlagField" form:"ldapFlagField"`
+	LdapTruthyValues      string `json:"ldapTruthyValues" form:"ldapTruthyValues"`
+	LdapInvertFlag        bool   `json:"ldapInvertFlag" form:"ldapInvertFlag"`
+	LdapInboundTags       string `json:"ldapInboundTags" form:"ldapInboundTags"`
+	LdapAutoCreate        bool   `json:"ldapAutoCreate" form:"ldapAutoCreate"`
+	LdapAutoDelete        bool   `json:"ldapAutoDelete" form:"ldapAutoDelete"`
+	LdapDefaultTotalGB    int    `json:"ldapDefaultTotalGB" form:"ldapDefaultTotalGB"`
+	LdapDefaultExpiryDays int    `json:"ldapDefaultExpiryDays" form:"ldapDefaultExpiryDays"`
+	LdapDefaultLimitIP    int    `json:"ldapDefaultLimitIP" form:"ldapDefaultLimitIP"`
 	// JSON subscription routing rules
 }
 

+ 12 - 13
web/service/inbound.go

@@ -1569,21 +1569,20 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, bo
 	return !clientOldEnabled, needRestart, nil
 }
 
-
 // SetClientEnableByEmail sets client enable state to desired value; returns (changed, needRestart, error)
 func (s *InboundService) SetClientEnableByEmail(clientEmail string, enable bool) (bool, bool, error) {
-    current, err := s.checkIsEnabledByEmail(clientEmail)
-    if err != nil {
-        return false, false, err
-    }
-    if current == enable {
-        return false, false, nil
-    }
-    newEnabled, needRestart, err := s.ToggleClientEnableByEmail(clientEmail)
-    if err != nil {
-        return false, needRestart, err
-    }
-    return newEnabled == enable, needRestart, nil
+	current, err := s.checkIsEnabledByEmail(clientEmail)
+	if err != nil {
+		return false, false, err
+	}
+	if current == enable {
+		return false, false, nil
+	}
+	newEnabled, needRestart, err := s.ToggleClientEnableByEmail(clientEmail)
+	if err != nil {
+		return false, needRestart, err
+	}
+	return newEnabled == enable, needRestart, nil
 }
 
 func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int) (bool, error) {

+ 40 - 40
web/service/setting.go

@@ -74,26 +74,26 @@ var defaultValueMap = map[string]string{
 	"externalTrafficInformEnable": "false",
 	"externalTrafficInformURI":    "",
 	// 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",
+	"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",
 }
 
 // SettingService provides business logic for application settings management.
@@ -565,83 +565,83 @@ func (s *SettingService) GetIpLimitEnable() (bool, error) {
 
 // LDAP exported getters
 func (s *SettingService) GetLdapEnable() (bool, error) {
-    return s.getBool("ldapEnable")
+	return s.getBool("ldapEnable")
 }
 
 func (s *SettingService) GetLdapHost() (string, error) {
-    return s.getString("ldapHost")
+	return s.getString("ldapHost")
 }
 
 func (s *SettingService) GetLdapPort() (int, error) {
-    return s.getInt("ldapPort")
+	return s.getInt("ldapPort")
 }
 
 func (s *SettingService) GetLdapUseTLS() (bool, error) {
-    return s.getBool("ldapUseTLS")
+	return s.getBool("ldapUseTLS")
 }
 
 func (s *SettingService) GetLdapBindDN() (string, error) {
-    return s.getString("ldapBindDN")
+	return s.getString("ldapBindDN")
 }
 
 func (s *SettingService) GetLdapPassword() (string, error) {
-    return s.getString("ldapPassword")
+	return s.getString("ldapPassword")
 }
 
 func (s *SettingService) GetLdapBaseDN() (string, error) {
-    return s.getString("ldapBaseDN")
+	return s.getString("ldapBaseDN")
 }
 
 func (s *SettingService) GetLdapUserFilter() (string, error) {
-    return s.getString("ldapUserFilter")
+	return s.getString("ldapUserFilter")
 }
 
 func (s *SettingService) GetLdapUserAttr() (string, error) {
-    return s.getString("ldapUserAttr")
+	return s.getString("ldapUserAttr")
 }
 
 func (s *SettingService) GetLdapVlessField() (string, error) {
-    return s.getString("ldapVlessField")
+	return s.getString("ldapVlessField")
 }
 
 func (s *SettingService) GetLdapSyncCron() (string, error) {
-    return s.getString("ldapSyncCron")
+	return s.getString("ldapSyncCron")
 }
 
 func (s *SettingService) GetLdapFlagField() (string, error) {
-    return s.getString("ldapFlagField")
+	return s.getString("ldapFlagField")
 }
 
 func (s *SettingService) GetLdapTruthyValues() (string, error) {
-    return s.getString("ldapTruthyValues")
+	return s.getString("ldapTruthyValues")
 }
 
 func (s *SettingService) GetLdapInvertFlag() (bool, error) {
-    return s.getBool("ldapInvertFlag")
+	return s.getBool("ldapInvertFlag")
 }
 
 func (s *SettingService) GetLdapInboundTags() (string, error) {
-    return s.getString("ldapInboundTags")
+	return s.getString("ldapInboundTags")
 }
 
 func (s *SettingService) GetLdapAutoCreate() (bool, error) {
-    return s.getBool("ldapAutoCreate")
+	return s.getBool("ldapAutoCreate")
 }
 
 func (s *SettingService) GetLdapAutoDelete() (bool, error) {
-    return s.getBool("ldapAutoDelete")
+	return s.getBool("ldapAutoDelete")
 }
 
 func (s *SettingService) GetLdapDefaultTotalGB() (int, error) {
-    return s.getInt("ldapDefaultTotalGB")
+	return s.getInt("ldapDefaultTotalGB")
 }
 
 func (s *SettingService) GetLdapDefaultExpiryDays() (int, error) {
-    return s.getInt("ldapDefaultExpiryDays")
+	return s.getInt("ldapDefaultExpiryDays")
 }
 
 func (s *SettingService) GetLdapDefaultLimitIP() (int, error) {
-    return s.getInt("ldapDefaultLimitIP")
+	return s.getInt("ldapDefaultLimitIP")
 }
 
 func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {

+ 33 - 33
web/service/user.go

@@ -7,7 +7,7 @@ import (
 	"github.com/mhsanaei/3x-ui/v2/database/model"
 	"github.com/mhsanaei/3x-ui/v2/logger"
 	"github.com/mhsanaei/3x-ui/v2/util/crypto"
-    ldaputil "github.com/mhsanaei/3x-ui/v2/util/ldap"
+	ldaputil "github.com/mhsanaei/3x-ui/v2/util/ldap"
 	"github.com/xlzd/gotp"
 	"gorm.io/gorm"
 )
@@ -49,38 +49,38 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode
 		return nil
 	}
 
-    // If LDAP enabled and local password check fails, attempt LDAP auth
-    if !crypto.CheckPasswordHash(user.Password, password) {
-        ldapEnabled, _ := s.settingService.GetLdapEnable()
-        if !ldapEnabled {
-            return nil
-        }
-
-        host, _ := s.settingService.GetLdapHost()
-        port, _ := s.settingService.GetLdapPort()
-        useTLS, _ := s.settingService.GetLdapUseTLS()
-        bindDN, _ := s.settingService.GetLdapBindDN()
-        ldapPass, _ := s.settingService.GetLdapPassword()
-        baseDN, _ := s.settingService.GetLdapBaseDN()
-        userFilter, _ := s.settingService.GetLdapUserFilter()
-        userAttr, _ := s.settingService.GetLdapUserAttr()
-
-        cfg := ldaputil.Config{
-            Host: host,
-            Port: port,
-            UseTLS: useTLS,
-            BindDN: bindDN,
-            Password: ldapPass,
-            BaseDN: baseDN,
-            UserFilter: userFilter,
-            UserAttr: userAttr,
-        }
-        ok, err := ldaputil.AuthenticateUser(cfg, username, password)
-        if err != nil || !ok {
-            return nil
-        }
-        // On successful LDAP auth, continue 2FA checks below
-    }
+	// If LDAP enabled and local password check fails, attempt LDAP auth
+	if !crypto.CheckPasswordHash(user.Password, password) {
+		ldapEnabled, _ := s.settingService.GetLdapEnable()
+		if !ldapEnabled {
+			return nil
+		}
+
+		host, _ := s.settingService.GetLdapHost()
+		port, _ := s.settingService.GetLdapPort()
+		useTLS, _ := s.settingService.GetLdapUseTLS()
+		bindDN, _ := s.settingService.GetLdapBindDN()
+		ldapPass, _ := s.settingService.GetLdapPassword()
+		baseDN, _ := s.settingService.GetLdapBaseDN()
+		userFilter, _ := s.settingService.GetLdapUserFilter()
+		userAttr, _ := s.settingService.GetLdapUserAttr()
+
+		cfg := ldaputil.Config{
+			Host:       host,
+			Port:       port,
+			UseTLS:     useTLS,
+			BindDN:     bindDN,
+			Password:   ldapPass,
+			BaseDN:     baseDN,
+			UserFilter: userFilter,
+			UserAttr:   userAttr,
+		}
+		ok, err := ldaputil.AuthenticateUser(cfg, username, password)
+		if err != nil || !ok {
+			return nil
+		}
+		// On successful LDAP auth, continue 2FA checks below
+	}
 
 	twoFactorEnable, err := s.settingService.GetTwoFactorEnable()
 	if err != nil {