Files
aiops/backend/controller/ldap_ad.go
T
2026-07-09 14:16:56 +08:00

155 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controller
import (
"fmt"
"strings"
"unicode/utf16"
"ezops/model"
"github.com/go-ldap/ldap/v3"
)
const (
ldapServerOpenLDAP = "openldap"
ldapServerAD = "ad"
)
// ldapIsAD 判断当前配置是否为 Active Directory // Logan.Li
func ldapIsAD(cfg *model.LDAPConfig) bool {
if cfg == nil {
return false
}
return strings.EqualFold(strings.TrimSpace(cfg.ServerType), ldapServerAD)
}
// ldapDefaultUserMapping 按目录类型返回默认字段映射 // Logan.Li
func ldapDefaultUserMapping(cfg *model.LDAPConfig) map[string]string {
if ldapIsAD(cfg) {
return map[string]string{
"username": "sAMAccountName",
"email": "mail",
"display_name": "displayName",
}
}
return map[string]string{
"username": "uid",
"email": "mail",
"display_name": "cn",
}
}
// ldapEncodeADPassword 将明文密码编码为 AD unicodePwd(UTF-16LE 带引号) // Logan.Li
func ldapEncodeADPassword(password string) []byte {
quoted := `"` + password + `"`
runes := utf16.Encode([]rune(quoted))
buf := make([]byte, len(runes)*2)
for i, r := range runes {
buf[i*2] = byte(r)
buf[i*2+1] = byte(r >> 8)
}
return buf
}
// ldapBuildUserDN 按目录类型构造用户 DN // Logan.Li
func ldapBuildUserDN(cfg *model.LDAPConfig, username, displayName, parentDN string) (string, error) {
parentDN = strings.TrimSpace(parentDN)
if parentDN == "" {
return "", fmt.Errorf("父级 DN 不能为空")
}
if ldapIsAD(cfg) {
cn := strings.TrimSpace(displayName)
if cn == "" {
cn = username
}
return fmt.Sprintf("CN=%s,%s", ldap.EscapeDN(cn), parentDN), nil
}
if strings.TrimSpace(username) == "" {
return "", fmt.Errorf("用户名不能为空")
}
return fmt.Sprintf("uid=%s,%s", ldap.EscapeFilter(username), parentDN), nil
}
// ldapOUListFilters 返回 OU/容器列表过滤器,兼容 OpenLDAP 与 AD // Logan.Li
func ldapOUListFilters(cfg *model.LDAPConfig) []string {
if ldapIsAD(cfg) {
return []string{
"(|(objectClass=organizationalUnit)(objectClass=container))",
}
}
return []string{"(objectClass=organizationalUnit)"}
}
// ldapOUEntryName 读取 OU/容器显示名 // Logan.Li
func ldapOUEntryName(entry *ldap.Entry) string {
if v := entry.GetAttributeValue("ou"); v != "" {
return v
}
return ldapEntryAttr(entry, "cn", "name")
}
// ldapAddOpenLDAPUser 在 OpenLDAP 中创建 inetOrgPerson 用户 // Logan.Li
func ldapAddOpenLDAPUser(conn *ldap.Conn, cfg *model.LDAPConfig, userDN, username, displayName, email, password string) error {
mapping := ldapUserMapping(cfg)
uidAttr := mapping["username"]
if uidAttr == "" {
uidAttr = "uid"
}
mailAttr := mapping["email"]
if mailAttr == "" {
mailAttr = "mail"
}
cn := displayName
if cn == "" {
cn = username
}
addReq := ldap.NewAddRequest(userDN, nil)
addReq.Attribute("objectClass", []string{"inetOrgPerson", "organizationalPerson", "person", "top"})
addReq.Attribute(uidAttr, []string{username})
addReq.Attribute("cn", []string{cn})
addReq.Attribute("sn", []string{cn})
if email != "" {
addReq.Attribute(mailAttr, []string{email})
}
addReq.Attribute("userPassword", []string{password})
return conn.Add(addReq)
}
// ldapAddADUser 在 Active Directory 中创建并启用用户账号 // Logan.Li
func ldapAddADUser(conn *ldap.Conn, userDN, username, displayName, email, password string) error {
cn := strings.TrimSpace(displayName)
if cn == "" {
cn = username
}
sn := cn
if idx := strings.LastIndex(cn, " "); idx > 0 {
sn = cn[idx+1:]
}
addReq := ldap.NewAddRequest(userDN, nil)
addReq.Attribute("objectClass", []string{"top", "person", "organizationalPerson", "user"})
addReq.Attribute("cn", []string{cn})
addReq.Attribute("sAMAccountName", []string{username})
addReq.Attribute("sn", []string{sn})
if email != "" {
addReq.Attribute("mail", []string{email})
}
// 先以禁用状态创建,设置密码后再启用 // Logan.Li
addReq.Attribute("userAccountControl", []string{"514"})
if err := conn.Add(addReq); err != nil {
return err
}
modPwd := ldap.NewModifyRequest(userDN, nil)
modPwd.Replace("unicodePwd", []string{string(ldapEncodeADPassword(password))})
if err := conn.Modify(modPwd); err != nil {
_ = conn.Del(ldap.NewDelRequest(userDN, nil))
return fmt.Errorf("设置 AD 密码失败(需 LDAPS 连接): %w", err)
}
modEnable := ldap.NewModifyRequest(userDN, nil)
modEnable.Replace("userAccountControl", []string{"512"})
return conn.Modify(modEnable)
}