Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormhsanaei <ho3ein.sanaei@gmail.com>2025-10-21 14:02:55 +0300
committermhsanaei <ho3ein.sanaei@gmail.com>2025-10-21 14:02:55 +0300
commit713a7328f60eec94c56b7ad6d17a942dffcfd944 (patch)
treefb945ed86c371f2742738685622e992c9743d840
parent01d4a7488dac0bb81dc8f31ec29d5c83f254b29e (diff)
gofmt
-rw-r--r--util/ldap/ldap.go238
-rw-r--r--web/entity/entity.go44
-rw-r--r--web/service/inbound.go25
-rw-r--r--web/service/setting.go80
-rw-r--r--web/service/user.go66
5 files changed, 225 insertions, 228 deletions
diff --git a/util/ldap/ldap.go b/util/ldap/ldap.go
index 1c7a20e7..795d0e23 100644
--- a/util/ldap/ldap.go
+++ b/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
}
-
-
diff --git a/web/entity/entity.go b/web/entity/entity.go
index de054e2b..42e2df85 100644
--- a/web/entity/entity.go
+++ b/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
}
diff --git a/web/service/inbound.go b/web/service/inbound.go
index 93414801..66e87a4f 100644
--- a/web/service/inbound.go
+++ b/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) {
diff --git a/web/service/setting.go b/web/service/setting.go
index fa85d58c..c8ce7896 100644
--- a/web/service/setting.go
+++ b/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 {
diff --git a/web/service/user.go b/web/service/user.go
index 87c46bf2..1bde69f6 100644
--- a/web/service/user.go
+++ b/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 {