ldap login support auto cover attributes

1. support cover attribute by ldap
2. add ldap auto regist config
This commit is contained in:
Feng_Qi 2020-03-22 23:43:06 +08:00
parent eb03ccc271
commit 7c86e8f58e
4 changed files with 146 additions and 75 deletions

View File

@ -14,11 +14,19 @@ ldap:
host: "ldap.example.org" host: "ldap.example.org"
port: 389 port: 389
baseDn: "dc=example,dc=org" baseDn: "dc=example,dc=org"
# AD: manange@example.org
bindUser: "cn=manager,dc=example,dc=org" bindUser: "cn=manager,dc=example,dc=org"
bindPass: "*******" bindPass: "*******"
# openldap: (&(uid=%s)) # openldap: (&(uid=%s))
# AD: (&(sAMAccountName=%s)) # AD: (&(sAMAccountName=%s))
authFilter: "(&(uid=%s))" authFilter: "(&(uid=%s))"
attributes:
dispname: "cn"
email: "mail"
phone: "mobile"
im: ""
coverAttributes: false
autoRegist: false
tls: false tls: false
startTLS: false startTLS: false

84
src/model/ldap.go Normal file
View File

@ -0,0 +1,84 @@
package model
import (
"crypto/tls"
"fmt"
"github.com/didi/nightingale/src/modules/monapi/config"
"gopkg.in/ldap.v3"
)
func genLdapAttributeSearchList() []string {
ldapAttributes := []string{}
attrs := config.Get().LDAP.Attributes
if attrs.Dispname != "" {
ldapAttributes = append(ldapAttributes, attrs.Dispname)
}
if attrs.Email != "" {
ldapAttributes = append(ldapAttributes, attrs.Email)
}
if attrs.Phone != "" {
ldapAttributes = append(ldapAttributes, attrs.Phone)
}
if attrs.Im != "" {
ldapAttributes = append(ldapAttributes, attrs.Im)
}
return ldapAttributes
}
func ldapReq(user, pass string) (*ldap.SearchResult, error) {
var conn *ldap.Conn
var err error
lc := config.Get().LDAP
addr := fmt.Sprintf("%s:%d", lc.Host, lc.Port)
if lc.TLS {
conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: true})
} else {
conn, err = ldap.Dial("tcp", addr)
}
if err != nil {
return nil, fmt.Errorf("cannot dial ldap: %v", err)
}
defer conn.Close()
if !lc.TLS && lc.StartTLS {
err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, fmt.Errorf("ldap.conn startTLS fail: %v", err)
}
}
err = conn.Bind(lc.BindUser, lc.BindPass)
if err != nil {
return nil, fmt.Errorf("bind ldap fail: %v, use %s", err, lc.BindUser)
}
searchRequest := ldap.NewSearchRequest(
lc.BaseDn, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(lc.AuthFilter, user), // The filter to apply
genLdapAttributeSearchList(), // A list attributes to retrieve
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return nil, fmt.Errorf("ldap search fail: %v", err)
}
if len(sr.Entries) == 0 {
return nil, fmt.Errorf("cannot find such user: %v", user)
}
if len(sr.Entries) > 1 {
return nil, fmt.Errorf("multi users is search, query user: %v", user)
}
if err := conn.Bind(sr.Entries[0].DN, pass); err != nil {
return nil, fmt.Errorf("password error")
}
return sr, nil
}

View File

@ -1,7 +1,6 @@
package model package model
import ( import (
"crypto/tls"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -118,6 +117,23 @@ func (u *User) CanModifyTeam(t *Team) (bool, error) {
return cnt > 0, err return cnt > 0, err
} }
func (u *User) CopyLdapAttr(sr *ldap.SearchResult) {
attrs := config.Get().LDAP.Attributes
if attrs.Dispname != "" {
u.Dispname = sr.Entries[0].GetAttributeValue(attrs.Dispname)
}
if attrs.Email != "" {
u.Email = sr.Entries[0].GetAttributeValue(attrs.Email)
}
if attrs.Phone != "" {
u.Phone = sr.Entries[0].GetAttributeValue(attrs.Phone)
}
if attrs.Im != "" {
u.Im = sr.Entries[0].GetAttributeValue(attrs.Im)
}
return
}
func InitRoot() { func InitRoot() {
var u User var u User
has, err := DB["uic"].Where("username=?", "root").Get(&u) has, err := DB["uic"].Where("username=?", "root").Get(&u)
@ -147,78 +163,31 @@ func InitRoot() {
} }
func LdapLogin(user, pass string) error { func LdapLogin(user, pass string) error {
var conn *ldap.Conn sr, err := ldapReq(user, pass)
var err error
lc := config.Get().LDAP
addr := fmt.Sprintf("%s:%d", lc.Host, lc.Port)
if lc.TLS {
conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: true})
} else {
conn, err = ldap.Dial("tcp", addr)
}
if err != nil {
return fmt.Errorf("cannot dial ldap: %v", err)
}
defer conn.Close()
if !lc.TLS && lc.StartTLS {
err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
return fmt.Errorf("ldap.conn startTLS fail: %v", err)
}
}
err = conn.Bind(lc.BindUser, lc.BindPass)
if err != nil {
return fmt.Errorf("bind ldap fail: %v, use %s", err, lc.BindUser)
}
searchRequest := ldap.NewSearchRequest(
lc.BaseDn, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(lc.AuthFilter, user), // The filter to apply
[]string{}, // A list attributes to retrieve
nil,
)
sr, err := conn.Search(searchRequest)
if err != nil {
return fmt.Errorf("ldap search fail: %v", err)
}
if len(sr.Entries) == 0 {
return fmt.Errorf("cannot find such user: %v", user)
}
if len(sr.Entries) > 1 {
return fmt.Errorf("multi users is search, query user: %v", user)
}
err = conn.Bind(sr.Entries[0].DN, pass)
if err != nil {
return fmt.Errorf("password error")
}
cnt, err := DB["uic"].Where("username=?", user).Count(new(User))
if err != nil { if err != nil {
return err return err
} }
if cnt > 0 { var u User
return nil has, err := DB["uic"].Where("username=?", user).Get(&u)
} if err != nil {
return err
u := &User{ }
Username: user, u.CopyLdapAttr(sr)
Password: "******", if has {
Dispname: "", if config.Get().LDAP.CoverAttributes {
Email: "", _, err := DB["uic"].Where("id=?", u.Id).Update(u)
return err
} else {
return nil
}
}
if !config.Get().LDAP.AutoRegist {
return fmt.Errorf("user has not be created, may be you should enable auto regist: %v", user)
} }
u.Username = user
u.Password = "******"
_, err = DB["uic"].Insert(u) _, err = DB["uic"].Insert(u)
return err return err
} }

View File

@ -64,14 +64,24 @@ type httpSection struct {
} }
type ldapSection struct { type ldapSection struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
BaseDn string `yaml:"baseDn"` BaseDn string `yaml:"baseDn"`
BindUser string `yaml:"bindUser"` BindUser string `yaml:"bindUser"`
BindPass string `yaml:"bindPass"` BindPass string `yaml:"bindPass"`
AuthFilter string `yaml:"authFilter"` AuthFilter string `yaml:"authFilter"`
TLS bool `yaml:"tls"` Attributes ldapAttributes `yaml:"attributes"`
StartTLS bool `yaml:"startTLS"` CoverAttributes bool `yaml:"coverAttributes"`
AutoRegist bool `yaml:"autoRegist"`
TLS bool `yaml:"tls"`
StartTLS bool `yaml:"startTLS"`
}
type ldapAttributes struct {
Dispname string `yaml:"dispname"`
Phone string `yaml:"phone"`
Email string `yaml:"email"`
Im string `yaml:"im"`
} }
var ( var (