feat: 添加导入原来openldap数据的能力 (#60)

This commit is contained in:
二丫讲梵 2022-07-10 11:01:27 +08:00 committed by GitHub
parent 38a70bc263
commit 637d6b2bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 379 additions and 4 deletions

View File

@ -104,3 +104,11 @@ func (m *GroupController) SyncFeiShuDepts(c *gin.Context) {
return logic.FeiShu.SyncFeiShuDepts(c, req)
})
}
//同步原ldap部门信息
func (m *GroupController) SyncOpenLdapDepts(c *gin.Context) {
req := new(request.SyncOpenLdapDeptsReq)
Run(c, req, func() (interface{}, interface{}) {
return logic.OpenLdap.SyncOpenLdapDepts(c, req)
})
}

View File

@ -88,3 +88,11 @@ func (uc UserController) SyncFeiShuUsers(c *gin.Context) {
return logic.FeiShu.SyncFeiShuUsers(c, req)
})
}
// 同步ldap用户信息
func (uc UserController) SyncOpenLdapUsers(c *gin.Context) {
req := new(request.SyncOpenLdapUserReq)
Run(c, req, func() (interface{}, interface{}) {
return logic.OpenLdap.SyncOpenLdapUsers(c, req)
})
}

View File

@ -24,6 +24,7 @@ var (
DingTalk = &DingTalkLogic{}
WeCom = &WeComLogic{}
FeiShu = &FeiShuLogic{}
OpenLdap = &OpenLdapLogic{}
Base = &BaseLogic{}
FieldRelation = &FieldRelationLogic{}

179
logic/openldap_logic.go Normal file
View File

@ -0,0 +1,179 @@
package logic
import (
"fmt"
"github.com/eryajf/go-ldap-admin/model"
"github.com/eryajf/go-ldap-admin/public/client/openldap"
"github.com/eryajf/go-ldap-admin/public/tools"
"github.com/eryajf/go-ldap-admin/service/isql"
"github.com/gin-gonic/gin"
)
type OpenLdapLogic struct {
}
//通过ldap获取部门信息
func (d *OpenLdapLogic) SyncOpenLdapDepts(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
// 1.获取所有部门
depts, err := openldap.GetAllDepts()
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("获取ldap部门列表失败%s", err.Error()))
}
// 2.将部门这个数组进行拆分一组是父ID为根的一组是父ID不为根的
var firstDepts []*openldap.Dept // 父ID为根的部门
var otherDepts []*openldap.Dept // 父ID不为根的部门
for _, dept := range depts {
if dept.ParentId == "1" {
firstDepts = append(firstDepts, dept)
} else {
otherDepts = append(otherDepts, dept)
}
}
// 3.先写父ID为根的再写父ID不为根的
for _, dept := range firstDepts {
err := d.AddDepts(&model.Group{
GroupName: dept.Name,
Remark: dept.Remark,
Creator: "system",
GroupType: "cn",
SourceDeptId: dept.Id,
Source: "openldap",
SourceDeptParentId: dept.ParentId,
GroupDN: dept.DN,
})
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncOpenLdapDepts添加根部门失败%s", err.Error()))
}
}
for _, dept := range otherDepts {
err := d.AddDepts(&model.Group{
GroupName: dept.Name,
Remark: dept.Remark,
Creator: "system",
GroupType: "cn",
SourceDeptId: dept.Id,
Source: "openldap",
SourceDeptParentId: dept.ParentId,
GroupDN: dept.DN,
})
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncOpenLdapDepts添加其他部门失败%s", err.Error()))
}
}
return nil, nil
}
// AddGroup 添加部门数据
func (d OpenLdapLogic) AddDepts(group *model.Group) error {
// 判断部门名称是否存在
parentGroup := new(model.Group)
err := isql.Group.Find(tools.H{"source_dept_id": group.SourceDeptParentId}, parentGroup)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("查询父级部门失败:%s", err.Error()))
}
if !isql.Group.Exist(tools.H{"source_dept_id": group.SourceDeptId}) {
group.ParentId = parentGroup.ID
// 在数据库中创建组
err = isql.Group.Add(group)
if err != nil {
return err
}
}
return nil
}
//根据现有数据库同步到的部门信息,开启用户同步
func (d OpenLdapLogic) SyncOpenLdapUsers(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
// 1.获取ldap用户列表
staffs, err := openldap.GetAllUsers()
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("获取ldap用户列表失败%s", err.Error()))
}
// 2.遍历用户,开始写入
for _, staff := range staffs {
groupIds, err := isql.Group.DeptIdsToGroupIds(staff.DepartmentIds)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("将部门ids转换为内部部门id失败%s", err.Error()))
}
// 根据角色id获取角色
roles, err := isql.Role.GetRolesByIds([]uint{2})
if err != nil {
return nil, tools.NewValidatorError(fmt.Errorf("根据角色ID获取角色信息失败:%s", err.Error()))
}
// 入库
err = d.AddUsers(&model.User{
Username: staff.Name,
Nickname: staff.DisplayName,
GivenName: staff.GivenName,
Mail: staff.Mail,
JobNumber: staff.EmployeeNumber,
Mobile: staff.Mobile,
PostalAddress: staff.PostalAddress,
Departments: staff.BusinessCategory,
Position: staff.DepartmentNumber,
Introduction: staff.CN,
Creator: "system",
Source: "openldap",
DepartmentId: tools.SliceToString(groupIds, ","),
SourceUserId: staff.Name,
SourceUnionId: staff.Name,
Roles: roles,
UserDN: staff.DN,
})
if err != nil {
return nil, tools.NewOperationError(fmt.Errorf("SyncOpenLdapUsers写入用户失败%s", err.Error()))
}
}
return nil, nil
}
// AddUser 添加用户数据
func (d OpenLdapLogic) AddUsers(user *model.User) error {
// 根据 unionid 查询用户,不存在则创建
if !isql.User.Exist(tools.H{"source_union_id": user.SourceUnionId}) {
if user.Departments == "" {
user.Departments = "默认:研发中心"
}
if user.GivenName == "" {
user.GivenName = user.Nickname
}
if user.PostalAddress == "" {
user.PostalAddress = "默认:地球"
}
if user.Position == "" {
user.Position = "默认:技术"
}
if user.Introduction == "" {
user.Introduction = user.Nickname
}
if user.JobNumber == "" {
user.JobNumber = "未启用"
}
// 先将用户添加到MySQL
err := isql.User.Add(user)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败" + err.Error()))
}
// 获取用户将要添加的分组
groups, err := isql.Group.GetGroupByIds(tools.StringToSlice(user.DepartmentId, ","))
if err != nil {
return tools.NewMySqlError(fmt.Errorf("根据部门ID获取部门信息失败" + err.Error()))
}
for _, group := range groups {
if group.GroupDN[:3] == "ou=" {
continue
}
// 先将用户和部门信息维护到MySQL
err := isql.Group.AddUserToGroup(group, []model.User{*user})
if err != nil {
return tools.NewMySqlError(fmt.Errorf("向MySQL添加用户到分组关系失败" + err.Error()))
}
}
return nil
}
return nil
}

View File

@ -6,8 +6,8 @@ import (
type Group struct {
gorm.Model
GroupName string `gorm:"type:varchar(20);comment:'分组名称'" json:"groupName"`
Remark string `gorm:"type:varchar(100);comment:'分组中文说明'" json:"remark"`
GroupName string `gorm:"type:varchar(128);comment:'分组名称'" json:"groupName"`
Remark string `gorm:"type:varchar(128);comment:'分组中文说明'" json:"remark"`
Creator string `gorm:"type:varchar(20);comment:'创建人'" json:"creator"`
GroupType string `gorm:"type:varchar(20);comment:'分组类型cn、ou'" json:"groupType"`
Users []*User `gorm:"many2many:group_users" json:"users"`

View File

@ -106,3 +106,7 @@ type SyncWeComDeptsReq struct {
// SyncFeiShuDeptsReq 同步飞书部门信息
type SyncFeiShuDeptsReq struct {
}
// SyncOpenLdapDeptsReq 同步原ldap部门信息
type SyncOpenLdapDeptsReq struct {
}

View File

@ -116,6 +116,10 @@ type SyncWeComUserReq struct {
type SyncFeiShuUserReq struct {
}
// SyncOpenLdapUserReq 同步ldap用户信息
type SyncOpenLdapUserReq struct {
}
// UserListReq 获取用户列表结构体
type UserListReq struct {
Username string `json:"username" form:"username"`

View File

@ -0,0 +1,143 @@
package openldap
import (
"fmt"
"strings"
"github.com/eryajf/go-ldap-admin/config"
"github.com/eryajf/go-ldap-admin/public/common"
ldap "github.com/go-ldap/ldap/v3"
)
type Dept struct {
DN string `json:"dn"`
Id string `json:"id"` // 部门ID
Name string `json:"name"` // 部门名称拼音
Remark string `json:"remark"` // 部门中文名
ParentId string `json:"parentid"` // 父部门ID
}
type User struct {
Name string `json:"name"`
DN string `json:"dn"`
CN string `json:"cn"`
SN string `json:"sn"`
Mobile string `json:"mobile"`
BusinessCategory string `json:"businessCategory"` // 业务类别,部门名字
DepartmentNumber string `json:"departmentNumber"` // 部门编号,此处可以存放员工的职位
Description string `json:"description"` // 描述
DisplayName string `json:"displayName"` // 展示名字,可以是中文名字
Mail string `json:"mail"` // 邮箱
EmployeeNumber string `json:"employeeNumber"` // 员工工号
GivenName string `json:"givenName"` // 给定名字,如果公司有花名,可以用这个字段
PostalAddress string `json:"postalAddress"` // 家庭住址
DepartmentIds []string `json:"department_ids"`
}
// GetAllDepts 获取所有部门
func GetAllDepts() (ret []*Dept, err error) {
// Construct query request
searchRequest := ldap.NewSearchRequest(
config.Conf.Ldap.BaseDN, // This is basedn, we will start searching from this node.
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, // Here several parameters are respectively scope, derefAliases, sizeLimit, timeLimit, typesOnly
"(&(objectClass=*))", // This is Filter for LDAP query
[]string{}, // Here are the attributes returned by the query, provided as an array. If empty, all attributes are returned
nil,
)
// Search through ldap built-in search
sr, err := common.LDAP.Search(searchRequest)
if err != nil {
return ret, err
}
// Refers to the entry that returns data. If it is greater than 0, the interface returns normally.
if len(sr.Entries) > 0 {
for _, v := range sr.Entries {
if v.DN == config.Conf.Ldap.BaseDN || v.DN == config.Conf.Ldap.AdminDN || strings.Contains(v.DN, config.Conf.Ldap.UserDN) {
continue
}
var ele Dept
ele.DN = v.DN
ele.Name = strings.Split(strings.Split(v.DN, ",")[0], "=")[1]
ele.Id = strings.Split(strings.Split(v.DN, ",")[0], "=")[1]
ele.Remark = v.GetAttributeValue("description")
if len(strings.Split(v.DN, ","))-len(strings.Split(config.Conf.Ldap.BaseDN, ",")) == 1 {
ele.ParentId = "openldap_1"
} else {
ele.ParentId = strings.Split(strings.Split(v.DN, ",")[1], "=")[1]
}
ret = append(ret, &ele)
}
}
return
}
// GetAllUsers 获取所有员工信息
func GetAllUsers() (ret []*User, err error) {
// Construct query request
searchRequest := ldap.NewSearchRequest(
config.Conf.Ldap.BaseDN, // This is basedn, we will start searching from this node.
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, // Here several parameters are respectively scope, derefAliases, sizeLimit, timeLimit, typesOnly
"(&(objectClass=*))", // This is Filter for LDAP query
[]string{}, // Here are the attributes returned by the query, provided as an array. If empty, all attributes are returned
nil,
)
// Search through ldap built-in search
sr, err := common.LDAP.Search(searchRequest)
if err != nil {
return ret, err
}
// Refers to the entry that returns data. If it is greater than 0, the interface returns normally.
if len(sr.Entries) > 0 {
for _, v := range sr.Entries {
if v.DN == config.Conf.Ldap.UserDN || !strings.Contains(v.DN, config.Conf.Ldap.UserDN) {
continue
}
name := strings.Split(strings.Split(v.DN, ",")[0], "=")[1]
deptIds, err := GetUserDeptIds(v.DN)
if err != nil {
return ret, err
}
ret = append(ret, &User{
Name: name,
DN: v.DN,
CN: v.GetAttributeValue("cn"),
SN: v.GetAttributeValue("sn"),
Mobile: v.GetAttributeValue("mobile"),
BusinessCategory: v.GetAttributeValue("businessCategory"),
DepartmentNumber: v.GetAttributeValue("departmentNumber"),
Description: v.GetAttributeValue("description"),
DisplayName: v.GetAttributeValue("displayName"),
Mail: v.GetAttributeValue("mail"),
EmployeeNumber: v.GetAttributeValue("employeeNumber"),
GivenName: v.GetAttributeValue("givenName"),
PostalAddress: v.GetAttributeValue("postalAddress"),
DepartmentIds: deptIds,
})
}
}
return
}
// GetUserDeptIds 获取用户所在的部门
func GetUserDeptIds(udn string) (ret []string, err error) {
// Construct query request
searchRequest := ldap.NewSearchRequest(
config.Conf.Ldap.BaseDN, // This is basedn, we will start searching from this node.
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, // Here several parameters are respectively scope, derefAliases, sizeLimit, timeLimit, typesOnly
fmt.Sprintf("(|(Member=%s)(uniqueMember=%s))", udn, udn), // This is Filter for LDAP query
[]string{}, // Here are the attributes returned by the query, provided as an array. If empty, all attributes are returned
nil,
)
// Search through ldap built-in search
sr, err := common.LDAP.Search(searchRequest)
if err != nil {
return ret, err
}
// Refers to the entry that returns data. If it is greater than 0, the interface returns normally.
if len(sr.Entries) > 0 {
for _, v := range sr.Entries {
ret = append(ret, strings.Split(strings.Split(v.DN, ",")[0], "=")[1])
}
}
return ret, nil
}

View File

@ -360,6 +360,13 @@ func InitData() {
Remark: "从飞书拉取用户信息",
Creator: "系统",
},
{
Method: "POST",
Path: "/user/syncOpenLdapUsers",
Category: "user",
Remark: "从openldap拉取用户信息",
Creator: "系统",
},
{
Method: "GET",
Path: "/group/list",
@ -444,6 +451,13 @@ func InitData() {
Remark: "从飞书拉取部门信息",
Creator: "系统",
},
{
Method: "POST",
Path: "/group/syncOpenLdapDepts",
Category: "group",
Remark: "从openldap拉取部门信息",
Creator: "系统",
},
{
Method: "GET",
Path: "/role/list",
@ -688,6 +702,18 @@ func InitData() {
groups := []model.Group{
{
Model: gorm.Model{ID: 1},
GroupName: "openldaproot",
Remark: "ldap根部门",
Creator: "system",
GroupType: "ou",
ParentId: 0,
SourceDeptId: "openldap_1",
Source: "openldap",
SourceDeptParentId: "openldap_0",
GroupDN: fmt.Sprintf("ou=%s,%s", "openldaproot", config.Conf.Ldap.BaseDN),
},
{
Model: gorm.Model{ID: 2},
GroupName: config.Conf.DingTalk.Flag + "root",
Remark: "钉钉根部门",
Creator: "system",
@ -699,7 +725,7 @@ func InitData() {
GroupDN: fmt.Sprintf("ou=%s,%s", config.Conf.DingTalk.Flag+"root", config.Conf.Ldap.BaseDN),
},
{
Model: gorm.Model{ID: 2},
Model: gorm.Model{ID: 3},
GroupName: "wecomroot",
Remark: "企业微信根部门",
Creator: "system",
@ -711,7 +737,7 @@ func InitData() {
GroupDN: fmt.Sprintf("ou=%s,%s", config.Conf.WeCom.Flag+"root", config.Conf.Ldap.BaseDN),
},
{
Model: gorm.Model{ID: 3},
Model: gorm.Model{ID: 4},
GroupName: config.Conf.FeiShu.Flag + "root",
Remark: "飞书根部门",
Creator: "system",

View File

@ -29,6 +29,7 @@ func InitGroupRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) g
group.POST("/syncDingTalkDepts", controller.Group.SyncDingTalkDepts) // 同步部门
group.POST("/syncWeComDepts", controller.Group.SyncWeComDepts) // 同步部门
group.POST("/syncFeiShuDepts", controller.Group.SyncFeiShuDepts) // 同步部门
group.POST("/syncOpenLdapDepts", controller.Group.SyncOpenLdapDepts) // 同步部门
}
return r

View File

@ -27,6 +27,7 @@ func InitUserRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gi
user.POST("/syncDingTalkUsers", controller.User.SyncDingTalkUsers) // 同步用户
user.POST("/syncWeComUsers", controller.User.SyncWeComUsers) // 同步用户
user.POST("/syncFeiShuUsers", controller.User.SyncFeiShuUsers) // 同步用户
user.POST("/syncOpenLdapUsers", controller.User.SyncOpenLdapUsers) // 同步用户
}
return r
}