package ldap import ( "encoding/json" "errors" "fmt" "github.com/astaxie/beego/logs" "github.com/go-ldap/ldap/v3" "nginx-ui/server/models" ) type Client struct { *ldap.Conn Url string Connected bool BaseDN string Admin string Password string ServerKey string pool *ConnectionPool } var ActiveClients = make(map[string]*Client) func createClient(url string, baseDN string, createPool bool) *Client { var client = &Client{ Url: url, BaseDN: baseDN, } if createPool { client.pool = NewConnectionPool(5, func() (interface{}, error) { c := createClient(url, baseDN, false) if c.Connected { return c, nil } return nil, errors.New("连接服务失败!") }) } conn, err := ldap.DialURL(client.Url) if err != nil { logs.Error("dialUrl fail: %v", err) client.Connected = false } else { client.Conn = conn client.Connected = true } return client } func GetActiveClient(server *models.LdapServer) (*Client, error) { client := ActiveClients[server.Key] if client == nil { client = createClient(server.Url, server.BaseDN, true) err := client.Bind(server.UserName, server.Password, true) if err != nil { logs.Error("Bind fail: %v", err) return nil, err } client.ServerKey = server.Key ActiveClients[server.Key] = client } return client, nil } func (c *Client) Close() { if c.Connected && c.Conn != nil { c.Conn.Close() } } func (c *Client) Acquire() (*Client, error) { inter, err := c.pool.Acquire() if err != nil { return nil, err } return inter.(*Client), err } // Bind 验证账号密码? func (c *Client) Bind(username string, password string, isAdmin bool) error { err := c.Conn.Bind(username, password) if err != nil { logs.Error("GSSAPIBind failed, err:%v", err) return err } if isAdmin { c.Admin = username c.Password = password } return nil } func createUser(entry *ldap.Entry) models.LdapUser { user := models.LdapUser{ Account: entry.GetAttributeValue("uid"), DN: entry.DN, Password: entry.GetAttributeValue("userPassword"), UserName: entry.GetAttributeValue("cn"), Mail: entry.GetAttributeValue("mail"), } jsonBytes, err := json.Marshal(entry.Attributes) if err != nil { logs.Error("marshal fail : %v", err) user.Remark = "attributes marshal fail: " + err.Error() } else { user.Attributes = string(jsonBytes) } return user } // Search 搜索用户 eg. (&(objectClass=organizationalPerson)) func (c *Client) Search(filter string) ([]models.LdapUser, error) { if filter == "" { filter = "(objectClass=*)" } searchRequest := ldap.NewSearchRequest( c.BaseDN, // The base dn to search ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, // The filter to apply []string{"*"}, // A list attributes to retrieve,"dn", "cn", "objectClass", nil, ) sr, err := c.Conn.Search(searchRequest) if err != nil { logs.Error("search fail : %v", err) return nil, err } var users []models.LdapUser for _, entry := range sr.Entries { user := createUser(entry) user.ServerKey = c.ServerKey users = append(users, user) } return users, nil } // 通过管理员修改密码,而非自行修改密码 func (c *Client) ModifyPasswordByAdmin(dn string, newPassword string) error { passwordModifyRequest := ldap.NewPasswordModifyRequest(dn, "", newPassword) _, err := c.PasswordModify(passwordModifyRequest) if err != nil { logs.Error("Password could not be changed: %s", err.Error()) return err } return nil } // ModifyPassword 自行修改密码 func (c *Client) ModifyPassword(userDN string, password string, newPassword string) error { l, err := c.Acquire() if err != nil { logs.Error(err) return err } err = l.Bind(userDN, password, false) if err != nil { logs.Error(err) return errors.New("密码验证失败:" + err.Error()) } passwordModifyRequest := ldap.NewPasswordModifyRequest("", password, newPassword) _, err = l.PasswordModify(passwordModifyRequest) if err != nil { logs.Error("Password could not be changed: %s", err.Error()) } return nil } func (c *Client) Modify() error { return nil } func (c *Client) Authentication(account string, password string) (*models.LdapUser, error) { // The username and password we want to check users, err := c.Search(fmt.Sprintf("(&(objectClass=*)(uid=%s))", ldap.EscapeFilter(account))) if err != nil { logs.Error("search fail: %v", err) return nil, err } if len(users) != 1 { logs.Error("User does not exist or too many entries returned") return nil, errors.New("未找到用户或者账号重复!") } userDN := users[0].DN client, err := c.Acquire() if err != nil { return nil, err } err = client.Bind(userDN, password, false) if err != nil { logs.Error("GSSAPIBind failed, err:%v", err) return nil, errors.New("登录失败,账号或者密码不正确!") } c.pool.Release(client) return &users[0], nil }