feat: enhance LDAP integration with new configuration options and browsing capabilities
This commit is contained in:
@@ -73,16 +73,12 @@ func (m *Manager) Login(database *db.DB, username, password string) (token, role
|
||||
cfg := m.cfg
|
||||
m.mu.RUnlock()
|
||||
|
||||
if cfg == nil || cfg.LDAP.Host == "" {
|
||||
return "", "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
if err := ldapBind(cfg.LDAP.Host, cfg.LDAP.Port, cfg.LDAP.BindDN, cfg.LDAP.BindPassword); err != nil {
|
||||
if cfg == nil || cfg.LDAP.Url == "" {
|
||||
return "", "", errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
if err := ldapUserBind(cfg, username, password); err != nil {
|
||||
return "", "", errors.New("invalid credentials")
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
tok, err := m.createSession(username, "user")
|
||||
@@ -90,44 +86,104 @@ func (m *Manager) Login(database *db.DB, username, password string) (token, role
|
||||
}
|
||||
|
||||
// TestLDAP tests an LDAP configuration by performing a service-account bind.
|
||||
func TestLDAP(host string, port int, bindDN, bindPassword string) error {
|
||||
return ldapBind(host, port, bindDN, bindPassword)
|
||||
}
|
||||
|
||||
func ldapBind(host string, port int, bindDN, bindPassword string) error {
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
|
||||
func TestLDAP(url string, adminUser, adminPassword string) error {
|
||||
l, err := ldap.DialURL(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
return l.Bind(bindDN, bindPassword)
|
||||
if adminUser != "" {
|
||||
return l.Bind(adminUser, adminPassword)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ldapUserBind(cfg *config.Config, username, password string) error {
|
||||
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", cfg.LDAP.Host, cfg.LDAP.Port))
|
||||
l, err := ldap.DialURL(cfg.LDAP.Url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
if err := l.Bind(cfg.LDAP.BindDN, cfg.LDAP.BindPassword); err != nil {
|
||||
return err
|
||||
if cfg.LDAP.AdminUser != "" {
|
||||
if err := l.Bind(cfg.LDAP.AdminUser, cfg.LDAP.AdminPass); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sr, err := l.Search(&ldap.SearchRequest{
|
||||
BaseDN: cfg.LDAP.BaseDN,
|
||||
Filter: fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username)),
|
||||
Scope: ldap.ScopeWholeSubtree,
|
||||
})
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
"", // Search from root or a specific BaseDN if needed. Assuming root here since URL can contain it, or we rely on LDAP configured properly
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(|(uid=%s)(sAMAccountName=%s))", ldap.EscapeFilter(username), ldap.EscapeFilter(username)),
|
||||
[]string{"dn"},
|
||||
nil,
|
||||
)
|
||||
|
||||
sr, err := l.Search(searchRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(sr.Entries) != 1 {
|
||||
if len(sr.Entries) == 0 {
|
||||
return errors.New("user not found in LDAP")
|
||||
}
|
||||
|
||||
// Bind to the exact DN of the user discovered
|
||||
return l.Bind(sr.Entries[0].DN, password)
|
||||
}
|
||||
|
||||
// BrowseLDAP returns users and groups from the LDAP server.
|
||||
func BrowseLDAP(url string, adminUser, adminPassword string) ([]string, []string, error) {
|
||||
l, err := ldap.DialURL(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
if adminUser != "" {
|
||||
if err := l.Bind(adminUser, adminPassword); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Search for Users
|
||||
userReq := ldap.NewSearchRequest(
|
||||
"", ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(|(objectClass=person)(objectClass=user))",
|
||||
[]string{"uid", "sAMAccountName", "cn"},
|
||||
nil,
|
||||
)
|
||||
var users []string
|
||||
if sr, err := l.Search(userReq); err == nil {
|
||||
for _, e := range sr.Entries {
|
||||
if v := e.GetAttributeValue("sAMAccountName"); v != "" {
|
||||
users = append(users, v)
|
||||
} else if v := e.GetAttributeValue("uid"); v != "" {
|
||||
users = append(users, v)
|
||||
} else if v := e.GetAttributeValue("cn"); v != "" {
|
||||
users = append(users, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search for Groups
|
||||
groupReq := ldap.NewSearchRequest(
|
||||
"", ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(|(objectClass=groupOfNames)(objectClass=group))",
|
||||
[]string{"cn"},
|
||||
nil,
|
||||
)
|
||||
var groups []string
|
||||
if sr, err := l.Search(groupReq); err == nil {
|
||||
for _, e := range sr.Entries {
|
||||
if v := e.GetAttributeValue("cn"); v != "" {
|
||||
groups = append(groups, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return users, groups, nil
|
||||
}
|
||||
|
||||
func (m *Manager) createSession(username, role string) (string, error) {
|
||||
token, err := generateToken()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user