feat: 添加导入原来openldap数据的能力 (#60)
This commit is contained in:
parent
38a70bc263
commit
637d6b2bbe
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ var (
|
|||
DingTalk = &DingTalkLogic{}
|
||||
WeCom = &WeComLogic{}
|
||||
FeiShu = &FeiShuLogic{}
|
||||
OpenLdap = &OpenLdapLogic{}
|
||||
Base = &BaseLogic{}
|
||||
FieldRelation = &FieldRelationLogic{}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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"`
|
||||
|
|
|
@ -106,3 +106,7 @@ type SyncWeComDeptsReq struct {
|
|||
// SyncFeiShuDeptsReq 同步飞书部门信息
|
||||
type SyncFeiShuDeptsReq struct {
|
||||
}
|
||||
|
||||
// SyncOpenLdapDeptsReq 同步原ldap部门信息
|
||||
type SyncOpenLdapDeptsReq struct {
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue