This commit is contained in:
eryajf 2022-05-28 22:50:10 +08:00
commit a3c32c6dcb
18 changed files with 441 additions and 118 deletions

View File

@ -31,9 +31,9 @@ mysql:
# 用户名 # 用户名
username: root username: root
# 密码 # 密码
password: 123465 password: 123456
# 数据库名 # 数据库名
database: go-ldap-admin database: go_ldap_admin
# 主机地址 # 主机地址
host: localhost host: localhost
# 端口 # 端口
@ -90,3 +90,5 @@ ldap:
ldap-admin-pass: "123456" ldap-admin-pass: "123456"
ldap-user-dn: "ou=people,dc=eryajf,dc=net" ldap-user-dn: "ou=people,dc=eryajf,dc=net"
ldap-group-dn: "ou=group,dc=eryajf,dc=net" ldap-group-dn: "ou=group,dc=eryajf,dc=net"
ldap-group-name-modify: false
ldap-user-name-modify: false

View File

@ -134,6 +134,8 @@ type LdapConfig struct {
LdapAdminPass string `mapstructure:"ldap-admin-pass" json:"ldapAdminPass"` LdapAdminPass string `mapstructure:"ldap-admin-pass" json:"ldapAdminPass"`
LdapUserDN string `mapstructure:"ldap-user-dn" json:"ldapUserDN"` LdapUserDN string `mapstructure:"ldap-user-dn" json:"ldapUserDN"`
LdapGroupDN string `mapstructure:"ldap-group-dn" json:"ldapGroupDN"` LdapGroupDN string `mapstructure:"ldap-group-dn" json:"ldapGroupDN"`
LdapGroupNameModify bool `mapstructure:"ldap-group-name-modify" json:"ldapGroupNameModify"`
LdapUserNameModify bool `mapstructure:"ldap-user-name-modify" json:"ldapUserNameModify"`
} }
type EmailConfig struct { type EmailConfig struct {
Host string `mapstructure:"host" json:"host"` Host string `mapstructure:"host" json:"host"`

View File

@ -33,13 +33,13 @@ func (m *GroupController) UserNoInGroup(c *gin.Context) {
}) })
} }
// // GetTree 接口树 // GetTree 接口树
// func (m *GroupController) GetTree(c *gin.Context) { func (m *GroupController) GetTree(c *gin.Context) {
// req := new(request.GroupGetTreeReq) req := new(request.GroupGetTreeReq)
// Run(c, req, func() (interface{}, interface{}) { Run(c, req, func() (interface{}, interface{}) {
// return logic.Group.GetTree(c, req) return logic.Group.GetTree(c, req)
// }) })
// } }
// Add 新建记录 // Add 新建记录
func (m *GroupController) Add(c *gin.Context) { func (m *GroupController) Add(c *gin.Context) {

View File

@ -90,3 +90,5 @@ ldap:
ldap-admin-pass: "123456" ldap-admin-pass: "123456"
ldap-user-dn: "ou=people,dc=eryajf,dc=net" ldap-user-dn: "ou=people,dc=eryajf,dc=net"
ldap-group-dn: "ou=group,dc=eryajf,dc=net" ldap-group-dn: "ou=group,dc=eryajf,dc=net"
ldap-group-name-modify: false
ldap-user-name-modify: false

24
go.mod
View File

@ -24,41 +24,19 @@ require (
) )
require ( require (
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/denisenkom/go-mssqldb v0.9.0 // indirect github.com/denisenkom/go-mssqldb v0.9.0 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/protobuf v1.4.3 // indirect github.com/golang/protobuf v1.4.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.8.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.0.7 // indirect github.com/jackc/pgproto3/v2 v2.0.7 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.6.2 // indirect
github.com/jackc/pgx/v4 v4.10.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.1 // indirect
github.com/json-iterator/go v1.1.10 // indirect github.com/json-iterator/go v1.1.10 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.4 // indirect github.com/magiconair/properties v1.8.4 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/spf13/afero v1.5.1 // indirect github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect github.com/ugorji/go v1.2.3 // indirect
github.com/ugorji/go/codec v1.2.3 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect

View File

@ -2,6 +2,7 @@ package logic
import ( import (
"fmt" "fmt"
"github.com/eryajf-world/go-ldap-admin/config"
"strings" "strings"
"github.com/eryajf-world/go-ldap-admin/model" "github.com/eryajf-world/go-ldap-admin/model"
@ -35,12 +36,20 @@ func (l GroupLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspE
} }
group := model.Group{ group := model.Group{
GroupType: r.GroupType,
ParentId: r.ParentId,
GroupName: r.GroupName, GroupName: r.GroupName,
Remark: r.Remark, Remark: r.Remark,
Creator: ctxUser.Username, Creator: ctxUser.Username,
} }
pdn := ""
err = ildap.Group.Add(&group) if group.ParentId > 0 {
pdn, err = isql.Group.GetGroupDn(r.ParentId, "")
if err != nil {
return nil, err.Error()
}
}
err = ildap.Group.Add(&group, pdn)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建分组失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建分组失败" + err.Error()))
} }
@ -95,6 +104,29 @@ func (l GroupLogic) List(c *gin.Context, req interface{}) (data interface{}, rsp
}, nil }, nil
} }
// GetTree 数据树
func (l GroupLogic) GetTree(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
r, ok := req.(*request.GroupGetTreeReq)
if !ok {
return nil, ReqAssertErr
}
_ = c
rList := request.GroupListReq{}
rList.PageNum = r.PageNum
rList.PageSize = r.PageSize
rList.GroupName = r.GroupName
rList.Remark = r.Remark
var groups []*model.Group
groups, err := isql.Group.List(&rList)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取资源列表失败: " + err.Error()))
}
tree := isql.GenGroupTree(0, groups)
return tree, nil
}
// Update 更新数据 // Update 更新数据
func (l GroupLogic) Update(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { func (l GroupLogic) Update(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) {
r, ok := req.(*request.GroupUpdateReq) r, ok := req.(*request.GroupUpdateReq)
@ -122,15 +154,26 @@ func (l GroupLogic) Update(c *gin.Context, req interface{}) (data interface{}, r
group := model.Group{ group := model.Group{
Model: oldData.Model, Model: oldData.Model,
GroupName: oldData.GroupName, GroupName: r.GroupName,
Remark: r.Remark, Remark: r.Remark,
Creator: ctxUser.Username, Creator: ctxUser.Username,
} GroupType: oldData.GroupType,
err = ildap.Group.Update(&group)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP更新分组失败"))
} }
oldGroupName := oldData.GroupName
oldRemark := oldData.Remark
dn, err := isql.Group.GetGroupDn(r.ID, "")
if err != nil {
return nil, err.Error()
}
err = ildap.Group.Update(&group, dn, oldGroupName, oldRemark)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP更新分组失败" + err.Error()))
}
//若配置了不允许修改分组名称,则不更新分组名称
if !config.Conf.Ldap.LdapGroupNameModify {
group.GroupName = oldGroupName
}
err = isql.Group.Update(&group) err = isql.Group.Update(&group)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向MySQL更新分组失败")) return nil, tools.NewLdapError(fmt.Errorf("向MySQL更新分组失败"))
@ -158,9 +201,19 @@ func (l GroupLogic) Delete(c *gin.Context, req interface{}) (data interface{}, r
return nil, tools.NewMySqlError(fmt.Errorf("获取分组列表失败: %s", err.Error())) return nil, tools.NewMySqlError(fmt.Errorf("获取分组列表失败: %s", err.Error()))
} }
for _, group := range groups { for _, group := range groups {
err := ildap.Group.Delete(group.GroupName) // 判断存在子分组,不允许删除
filter := tools.H{"parent_id": int(group.ID)}
if isql.Group.Exist(filter) {
return nil, tools.NewMySqlError(fmt.Errorf("存在子分组,不允许删除!"))
}
dn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP删除分组失败")) return nil, err.Error()
}
gdn := fmt.Sprintf("%s,%s", dn, config.Conf.Ldap.LdapBaseDN)
err = ildap.Group.Delete(gdn)
if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP删除分组失败" + err.Error()))
} }
} }
// 删除接口 // 删除接口
@ -168,6 +221,7 @@ func (l GroupLogic) Delete(c *gin.Context, req interface{}) (data interface{}, r
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("删除接口失败: %s", err.Error())) return nil, tools.NewMySqlError(fmt.Errorf("删除接口失败: %s", err.Error()))
} }
// TODO: 删除用户组关系
return nil, nil return nil, nil
} }
@ -197,7 +251,16 @@ func (l GroupLogic) AddUser(c *gin.Context, req interface{}) (data interface{},
} }
for _, user := range users { for _, user := range users {
err := ildap.Group.AddUserToGroup(group.GroupName, user.Username) gdn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil {
return nil, err.Error()
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
udn := config.Conf.Ldap.LdapAdminDN
if user.Username != "admin" {
udn = fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN)
}
err = ildap.Group.AddUserToGroup(gdn, udn)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error()))
@ -236,14 +299,17 @@ func (l GroupLogic) RemoveUser(c *gin.Context, req interface{}) (data interface{
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error())) return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
} }
gdn, err := isql.Group.GetGroupDn(r.GroupID, "")
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
for _, user := range users { for _, user := range users {
err := ildap.Group.RemoveUserFromGroup(group.GroupName, user.Username) err := ildap.Group.RemoveUserFromGroup(gdn, user.Username)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("将用户从ldap移除失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("将用户从ldap移除失败" + err.Error()))
} }
} }
err = isql.Group.RemoveUserFromGroup(group, users) err = isql.Group.RemoveUserFromGroup(group, users)
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("将用户从MySQL移除失败: %s", err.Error())) return nil, tools.NewMySqlError(fmt.Errorf("将用户从MySQL移除失败: %s", err.Error()))

View File

@ -1,6 +1,7 @@
package logic package logic
import ( import (
"errors"
"fmt" "fmt"
"github.com/eryajf-world/go-ldap-admin/config" "github.com/eryajf-world/go-ldap-admin/config"
@ -96,17 +97,48 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr
Introduction: r.Introduction, Introduction: r.Introduction,
Status: r.Status, Status: r.Status,
Creator: ctxUser.Username, Creator: ctxUser.Username,
DepartmentId: r.DepartmentId,
Source: r.Source,
Roles: roles, Roles: roles,
} }
if user.Source == "" {
user.Source = "platform"
}
//先识别用户选择的部门是否是OU开头
dn, err := isql.Group.GetGroupDn(r.DepartmentId, "")
if err != nil {
return nil, err.Error()
}
gdn := fmt.Sprintf("%s,%s", dn, config.Conf.Ldap.LdapBaseDN)
if gdn[:3] == "ou=" {
return nil, errors.New("不能添加用户到OU组织单元")
}
//先创建用户到默认分组
err = ildap.User.Add(&user) err = ildap.User.Add(&user)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建用户失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("向LDAP创建用户失败" + err.Error()))
}
//根据选择的部门,添加到部门内
err = ildap.Group.AddUserToGroup(gdn, fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN))
if err != nil {
return nil, err.Error()
} }
err = isql.User.Add(&user) err = isql.User.Add(&user)
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败")) return nil, tools.NewMySqlError(fmt.Errorf("向MySQL创建用户失败" + err.Error()))
}
//根据部门分配,将用户和部门信息维护到部门关系表里面
users := []model.User{}
users = append(users, user)
depart := new(model.Group)
filter := tools.H{"id": int(r.DepartmentId)}
err = isql.Group.Find(filter, depart)
if err != nil {
return "", tools.NewMySqlError(err)
}
err = isql.Group.AddUserToGroup(depart, users)
if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("向MySQL添加用户到分组关系失败" + err.Error()))
} }
return nil, nil return nil, nil
} }
@ -121,16 +153,16 @@ func (l UserLogic) List(c *gin.Context, req interface{}) (data interface{}, rspE
users, err := isql.User.List(r) users, err := isql.User.List(r)
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取用户列表失败")) return nil, tools.NewMySqlError(fmt.Errorf("获取用户列表失败" + err.Error()))
} }
rets := make([]model.User, 0) rets := make([]model.User, 0)
for _, user := range users { for _, user := range users {
rets = append(rets, *user) rets = append(rets, *user)
} }
count, err := isql.User.Count() count, err := isql.User.ListCount(r)
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("获取用户总数失败")) return nil, tools.NewMySqlError(fmt.Errorf("获取用户总数失败" + err.Error()))
} }
return response.UserListRsp{ return response.UserListRsp{
@ -194,6 +226,7 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
user := model.User{ user := model.User{
Model: oldData.Model, Model: oldData.Model,
Username: r.Username,
Nickname: r.Nickname, Nickname: r.Nickname,
GivenName: r.GivenName, GivenName: r.GivenName,
Mail: r.Mail, Mail: r.Mail,
@ -205,6 +238,8 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
Position: r.Position, Position: r.Position,
Introduction: r.Introduction, Introduction: r.Introduction,
Creator: ctxUser.Username, Creator: ctxUser.Username,
DepartmentId: r.DepartmentId,
Source: oldData.Source,
Roles: roles, Roles: roles,
} }
@ -230,19 +265,108 @@ func (l UserLogic) Update(c *gin.Context, req interface{}) (data interface{}, rs
return nil, tools.NewValidatorError(fmt.Errorf("用户不能把别的用户角色等级更新得比自己高或相等")) return nil, tools.NewValidatorError(fmt.Errorf("用户不能把别的用户角色等级更新得比自己高或相等"))
} }
} }
err = ildap.User.Update(oldData.Username, &user) err = ildap.User.Update(oldData.Username, &user)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP更新用户失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("在LDAP更新用户失败" + err.Error()))
} }
// 更新用户 // 更新用户
if !config.Conf.Ldap.LdapUserNameModify {
user.Username = oldData.Username
}
err = isql.User.Update(&user) err = isql.User.Update(&user)
if err != nil { if err != nil {
return nil, tools.NewMySqlError(fmt.Errorf("在MySQL更新用户失败" + err.Error())) return nil, tools.NewMySqlError(fmt.Errorf("在MySQL更新用户失败" + err.Error()))
}
//判断部门信息是否有变化有变化则更新相应的数据库
if oldData.DepartmentId != r.DepartmentId {
//从旧组中删除
err = l.RemoveUserToGroup(oldData.DepartmentId, []uint{r.ID})
//添加到新分组中
err = l.AddUserToGroup(r.DepartmentId, []uint{r.ID})
if err != nil {
return nil, err
}
}
return nil, nil
} }
return nil, nil // RemoveUser 移除用户
func (l UserLogic) RemoveUserToGroup(groupId uint, userIds []uint) error {
filter := tools.H{"id": groupId}
if !isql.Group.Exist(filter) {
return tools.NewMySqlError(fmt.Errorf("分组不存在"))
}
users, err := isql.User.GetUserByIds(userIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取用户列表失败: %s", err.Error()))
}
group := new(model.Group)
err = isql.Group.Find(filter, group)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn, err := isql.Group.GetGroupDn(groupId, "")
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
for _, user := range users {
err := ildap.Group.RemoveUserFromGroup(gdn, user.Username)
if err != nil {
return tools.NewLdapError(fmt.Errorf("将用户从ldap移除失败" + err.Error()))
}
}
err = isql.Group.RemoveUserFromGroup(group, users)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("将用户从MySQL移除失败: %s", err.Error()))
}
return nil
}
//将用户添加到分组中
func (l UserLogic) AddUserToGroup(groupId uint, userIds []uint) error {
filter := tools.H{"id": groupId}
if !isql.Group.Exist(filter) {
return tools.NewMySqlError(fmt.Errorf("分组不存在"))
}
users, err := isql.User.GetUserByIds(userIds)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取用户列表失败: %s", err.Error()))
}
group := new(model.Group)
err = isql.Group.Find(filter, group)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("获取分组失败: %s", err.Error()))
}
for _, user := range users {
gdn, err := isql.Group.GetGroupDn(group.ID, "")
if err != nil {
return err
}
gdn = fmt.Sprintf("%s,%s", gdn, config.Conf.Ldap.LdapBaseDN)
udn := config.Conf.Ldap.LdapAdminDN
if user.Username != "admin" {
udn = fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN)
}
err = ildap.Group.AddUserToGroup(gdn, udn)
if err != nil {
return tools.NewLdapError(fmt.Errorf("向LDAP添加用户到分组失败" + err.Error()))
}
}
err = isql.Group.AddUserToGroup(group, users)
if err != nil {
return tools.NewMySqlError(fmt.Errorf("添加用户到分组失败: %s", err.Error()))
}
return nil
} }
// Delete 删除数据 // Delete 删除数据
@ -388,7 +512,7 @@ func (l UserLogic) ChangeUserStatus(c *gin.Context, req interface{}) (data inter
return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error()))
} }
} else { } else {
err := ildap.User.Add(user) err = ildap.User.Add(user)
if err != nil { if err != nil {
return nil, tools.NewLdapError(fmt.Errorf("在LDAP添加用户失败" + err.Error())) return nil, tools.NewLdapError(fmt.Errorf("在LDAP添加用户失败" + err.Error()))
} }

View File

@ -7,5 +7,8 @@ type Group struct {
GroupName string `gorm:"type:varchar(20);comment:'分组名称'" json:"groupName"` GroupName string `gorm:"type:varchar(20);comment:'分组名称'" json:"groupName"`
Remark string `gorm:"type:varchar(100);comment:'分组中文说明'" json:"remark"` Remark string `gorm:"type:varchar(100);comment:'分组中文说明'" json:"remark"`
Creator string `gorm:"type:varchar(20);comment:'创建人'" json:"creator"` 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"` Users []*User `gorm:"many2many:group_users" json:"users"`
ParentId uint `gorm:"default:0;comment:'父组编号(编号为0时表示根组)'" json:"parentId"`
Children []*Group `gorm:"-" json:"children"`
} }

View File

@ -4,19 +4,21 @@ import "gorm.io/gorm"
type User struct { type User struct {
gorm.Model gorm.Model
Username string `gorm:"type:varchar(10);not null;unique" json:"username"` // 用户名 Username string `gorm:"type:varchar(50);not null;unique;comment:'用户名'" json:"username"` // 用户名
Password string `gorm:"size:255;not null" json:"password"` // 用户密码 Password string `gorm:"size:255;not null;comment:'用户密码'" json:"password"` // 用户密码
Nickname string `gorm:"type:varchar(10)" json:"nickname"` // 昵称 Nickname string `gorm:"type:varchar(50);comment:'中文名'" json:"nickname"` // 昵称
GivenName string `gorm:"type:varchar(10)" json:"givenName"` // 花名,如果有的话,没有的话用昵称占位 GivenName string `gorm:"type:varchar(50);comment:'花名'" json:"givenName"` // 花名,如果有的话,没有的话用昵称占位
Mail string `gorm:"type:varchar(20)" json:"mail"` // 邮箱 Mail string `gorm:"type:varchar(20);comment:'邮箱'" json:"mail"` // 邮箱
JobNumber string `gorm:"type:varchar(5)" json:"jobNumber"` // 工号 JobNumber string `gorm:"type:varchar(20);comment:'工号'" json:"jobNumber"` // 工号
Mobile string `gorm:"type:varchar(11);not null;unique" json:"mobile"` // 手机号 Mobile string `gorm:"type:varchar(11);not null;unique;comment:'手机号'" json:"mobile"` // 手机号
Avatar string `gorm:"type:varchar(255)" json:"avatar"` // 头像 Avatar string `gorm:"type:varchar(255);comment:'头像'" json:"avatar"` // 头像
PostalAddress string `gorm:"type:varchar(255)" json:"postalAddress"` // 地址 PostalAddress string `gorm:"type:varchar(255);comment:'地址'" json:"postalAddress"` // 地址
Departments string `gorm:"type:varchar(128)" json:"departments"` // 部门 Departments string `gorm:"type:varchar(128);comment:'部门'" json:"departments"` // 部门
Position string `gorm:"type:varchar(128)" json:"position"` // 职位 Position string `gorm:"type:varchar(128);comment:'职位'" json:"position"` // 职位
Introduction string `gorm:"type:varchar(255)" json:"introduction"` // 个人简介 Introduction string `gorm:"type:varchar(255);comment:'个人简介'" json:"introduction"` // 个人简介
Status uint `gorm:"type:tinyint(1);default:1;comment:'1在职, 2离职'" json:"status"` // 状态 Status uint `gorm:"type:tinyint(1);default:1;comment:'状态:1在职, 2离职'" json:"status"` // 状态
Creator string `gorm:"type:varchar(20);" json:"creator"` // 创建者 Creator string `gorm:"type:varchar(20);;comment:'创建者'" json:"creator"` // 创建者
Source string `gorm:"type:varchar(50);comment:'用户来源dingTalk、weCom、ldap、platform'" json:"source"` // 来源
DepartmentId uint `gorm:"type:int(20);not null;comment:'部门id'" json:"departmentId"` // 部门id
Roles []*Role `gorm:"many2many:user_roles" json:"roles"` // 角色 Roles []*Role `gorm:"many2many:user_roles" json:"roles"` // 角色
} }

View File

@ -331,6 +331,13 @@ func InitData() {
Remark: "获取分组列表", Remark: "获取分组列表",
Creator: "系统", Creator: "系统",
}, },
{
Method: "GET",
Path: "/group/tree",
Category: "group",
Remark: "获取分组列表树",
Creator: "系统",
},
{ {
Method: "POST", Method: "POST",
Path: "/group/add", Path: "/group/add",
@ -549,6 +556,7 @@ func InitData() {
"/user/list", "/user/list",
"/user/changePwd", "/user/changePwd",
"/group/list", "/group/list",
"/group/tree",
"/group/useringroup", "/group/useringroup",
"/group/usernoingroup", "/group/usernoingroup",
"/role/list", "/role/list",

View File

@ -17,7 +17,7 @@ func init() {
// NewPageOption 创建一个分页参数 // NewPageOption 创建一个分页参数
func NewPageOption(pageNum, pageSize int) *PageOption { func NewPageOption(pageNum, pageSize int) *PageOption {
if !(pageSize > 0 && pageSize < 1000) || pageNum < 0 || pageSize <= 0 { if !(pageSize > 0 && pageSize <= 1000) || pageNum < 0 || pageSize <= 0 {
return defaultOptions return defaultOptions
} }

View File

@ -16,6 +16,7 @@ func InitGroupRoutes(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) g
group.Use(middleware.CasbinMiddleware()) group.Use(middleware.CasbinMiddleware())
{ {
group.GET("/list", controller.Group.List) group.GET("/list", controller.Group.List)
group.GET("/tree", controller.Group.GetTree)
group.POST("/add", controller.Group.Add) group.POST("/add", controller.Group.Add)
group.POST("/update", controller.Group.Update) group.POST("/update", controller.Group.Update)
group.POST("/delete", controller.Group.Delete) group.POST("/delete", controller.Group.Delete)

View File

@ -1,8 +1,8 @@
package ildap package ildap
import ( import (
"errors"
"fmt" "fmt"
"github.com/eryajf-world/go-ldap-admin/config" "github.com/eryajf-world/go-ldap-admin/config"
"github.com/eryajf-world/go-ldap-admin/model" "github.com/eryajf-world/go-ldap-admin/model"
"github.com/eryajf-world/go-ldap-admin/public/common" "github.com/eryajf-world/go-ldap-admin/public/common"
@ -13,46 +13,78 @@ import (
type GroupService struct{} type GroupService struct{}
// Add 添加资源 // Add 添加资源
func (x GroupService) Add(g *model.Group) error { func (x GroupService) Add(g *model.Group, pdn string) error { //organizationalUnit
add := ldap.NewAddRequest(fmt.Sprintf("cn=%s,%s", g.GroupName, config.Conf.Ldap.LdapGroupDN), nil) parentDn := config.Conf.Ldap.LdapBaseDN
add.Attribute("objectClass", []string{"groupOfNames", "top"}) // 如果定义了 groupOfNAmes那么必须指定member否则报错如下object class 'groupOfNames' requires attribute 'member' if pdn != "" {
add.Attribute("cn", []string{g.GroupName}) parentDn = fmt.Sprintf("%s,%s", pdn, config.Conf.Ldap.LdapBaseDN)
}
dn := fmt.Sprintf("%s=%s,%s", g.GroupType, g.GroupName, parentDn)
add := ldap.NewAddRequest(dn, nil)
if g.GroupType == "ou" {
add.Attribute("objectClass", []string{"organizationalUnit", "top"}) // 如果定义了 groupOfNAmes那么必须指定member否则报错如下object class 'groupOfNames' requires attribute 'member'
}
if g.GroupType == "cn" {
add.Attribute("objectClass", []string{"groupOfUniqueNames", "top"})
add.Attribute("uniqueMember", []string{config.Conf.Ldap.LdapAdminDN}) // 所以这里创建组的时候默认将admin加入其中以免创建时没有人员而报上边的错误
}
add.Attribute(g.GroupType, []string{g.GroupName})
add.Attribute("description", []string{g.Remark}) add.Attribute("description", []string{g.Remark})
add.Attribute("member", []string{config.Conf.Ldap.LdapAdminDN}) // 所以这里创建组的时候默认将admin加入其中以免创建时没有人员而报上边的错误
return common.LDAP.Add(add) return common.LDAP.Add(add)
} }
// UpdateGroup 更新一个分组 // UpdateGroup 更新一个分组
func (x GroupService) Update(g *model.Group) error { func (x GroupService) Update(g *model.Group, pdn string, oldGroupName, oldRemark string) error {
modify := ldap.NewModifyRequest(fmt.Sprintf("cn=%s,%s", g.GroupName, config.Conf.Ldap.LdapGroupDN), nil) parentDn := "," + config.Conf.Ldap.LdapBaseDN
if pdn != "" {
parentDn = fmt.Sprintf("%s,%s", pdn, config.Conf.Ldap.LdapBaseDN)
}
//默认更新remark字段
if g.Remark != oldRemark {
modify := ldap.NewModifyRequest(parentDn, nil)
modify.Replace("description", []string{g.Remark}) modify.Replace("description", []string{g.Remark})
return common.LDAP.Modify(modify) err := common.LDAP.Modify(modify)
if err != nil {
return err
}
}
// 如果配置文件允许修改分组名称,且分组名称发生了变化,那么执行修改分组名称
if config.Conf.Ldap.LdapGroupNameModify && g.GroupName != oldGroupName {
rdn := fmt.Sprintf("%s=%s", g.GroupType, g.GroupName)
modify := ldap.NewModifyDNRequest(parentDn, rdn, true, "")
err := common.LDAP.ModifyDN(modify)
if err != nil {
return err
}
}
return nil
} }
// Delete 删除资源 // Delete 删除资源
func (x GroupService) Delete(group string) error { func (x GroupService) Delete(pdn string) error {
del := ldap.NewDelRequest(fmt.Sprintf("cn=%s,%s", group, config.Conf.Ldap.LdapGroupDN), nil) parentDn := "," + config.Conf.Ldap.LdapBaseDN
if pdn != "" {
parentDn = fmt.Sprintf("%s,%s", pdn, config.Conf.Ldap.LdapBaseDN)
}
del := ldap.NewDelRequest(parentDn, nil)
return common.LDAP.Del(del) return common.LDAP.Del(del)
} }
// AddUserToGroup 添加用户到分组 // AddUserToGroup 添加用户到分组
func (x GroupService) AddUserToGroup(group, user string) error { func (x GroupService) AddUserToGroup(dn, udn string) error {
udn := fmt.Sprintf("uid=%s,%s", user, config.Conf.Ldap.LdapUserDN) //判断dn是否以ou开头
if user == "admin" { if dn[:3] == "ou=" {
udn = config.Conf.Ldap.LdapAdminDN return errors.New("不能添加用户到OU组织单元")
} }
gdn := fmt.Sprintf("cn=%s,%s", group, config.Conf.Ldap.LdapGroupDN) newmr := ldap.NewModifyRequest(dn, nil)
newmr := ldap.NewModifyRequest(gdn, nil) newmr.Add("uniqueMember", []string{udn})
newmr.Add("member", []string{udn})
return common.LDAP.Modify(newmr) return common.LDAP.Modify(newmr)
} }
// DelUserFromGroup 将用户从分组删除 // DelUserFromGroup 将用户从分组删除
func (x GroupService) RemoveUserFromGroup(group, user string) error { func (x GroupService) RemoveUserFromGroup(gdn, user string) error {
udn := fmt.Sprintf("uid=%s,%s", user, config.Conf.Ldap.LdapUserDN) udn := fmt.Sprintf("uid=%s,%s", user, config.Conf.Ldap.LdapUserDN)
gdn := fmt.Sprintf("cn=%s,%s", group, config.Conf.Ldap.LdapGroupDN)
newmr := ldap.NewModifyRequest(gdn, nil) newmr := ldap.NewModifyRequest(gdn, nil)
newmr.Delete("member", []string{udn}) newmr.Delete("uniqueMember", []string{udn})
return common.LDAP.Modify(newmr) return common.LDAP.Modify(newmr)
} }

View File

@ -29,7 +29,6 @@ func (x UserService) Add(user *model.User) error {
if user.Introduction == "" { if user.Introduction == "" {
user.Introduction = user.Nickname user.Introduction = user.Nickname
} }
add := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN), nil) add := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, config.Conf.Ldap.LdapUserDN), nil)
add.Attribute("objectClass", []string{"inetOrgPerson"}) add.Attribute("objectClass", []string{"inetOrgPerson"})
add.Attribute("cn", []string{user.Nickname}) add.Attribute("cn", []string{user.Nickname})
@ -62,8 +61,15 @@ func (x UserService) Update(oldusername string, user *model.User) error {
modify.Replace("givenName", []string{user.GivenName}) modify.Replace("givenName", []string{user.GivenName})
modify.Replace("postalAddress", []string{user.PostalAddress}) modify.Replace("postalAddress", []string{user.PostalAddress})
modify.Replace("mobile", []string{user.Mobile}) modify.Replace("mobile", []string{user.Mobile})
modify.Replace("uid", []string{oldusername}) err := common.LDAP.Modify(modify)
return common.LDAP.Modify(modify) if err != nil {
return err
}
if config.Conf.Ldap.LdapUserNameModify && oldusername != user.Username {
modifyDn := ldap.NewModifyDNRequest(fmt.Sprintf("uid=%s,%s", oldusername, config.Conf.Ldap.LdapUserDN), fmt.Sprintf("uid=%s", user.Username), true, "")
return common.LDAP.ModifyDN(modifyDn)
}
return nil
} }
// Delete 删除资源 // Delete 删除资源

View File

@ -34,6 +34,44 @@ func (s GroupService) List(req *request.GroupListReq) ([]*model.Group, error) {
return list, err return list, err
} }
// 拼装dn信息
func (s GroupService) GetGroupDn(groupId uint, oldDn string) (dn string, e error) {
depart := new(model.Group)
filter := tools.H{"id": int(groupId)}
err := s.Find(filter, depart)
if err != nil {
return "", tools.NewMySqlError(err)
}
if oldDn == "" {
dn = fmt.Sprintf("%s=%s", depart.GroupType, depart.GroupName)
} else {
dn = fmt.Sprintf("%s,%s=%s", oldDn, depart.GroupType, depart.GroupName)
}
if depart.ParentId > 0 {
tempDn, err := s.GetGroupDn(depart.ParentId, dn)
if err != nil {
return dn, err
}
dn = tempDn
fmt.Println(tempDn)
}
return dn, nil
}
// GenGroupTree 生成分组树
func GenGroupTree(parentId uint, groups []*model.Group) []*model.Group {
tree := make([]*model.Group, 0)
for _, g := range groups {
if g.ParentId == parentId {
children := GenGroupTree(g.ID, groups)
g.Children = children
tree = append(tree, g)
}
}
return tree
}
// Count 获取数据总数 // Count 获取数据总数
func (s GroupService) Count() (int64, error) { func (s GroupService) Count() (int64, error) {
var count int64 var count int64

View File

@ -25,6 +25,8 @@ var userInfoCache = cache.New(24*time.Hour, 48*time.Hour)
// Add 添加资源 // Add 添加资源
func (s UserService) Add(user *model.User) error { func (s UserService) Add(user *model.User) error {
user.Password = tools.NewGenPasswd(user.Password) user.Password = tools.NewGenPasswd(user.Password)
//result := common.DB.Create(user)
//return user.ID, result.Error
return common.DB.Create(user).Error return common.DB.Create(user).Error
} }
@ -45,6 +47,14 @@ func (s UserService) List(req *request.UserListReq) ([]*model.User, error) {
if mobile != "" { if mobile != "" {
db = db.Where("mobile LIKE ?", fmt.Sprintf("%%%s%%", mobile)) db = db.Where("mobile LIKE ?", fmt.Sprintf("%%%s%%", mobile))
} }
departmentId := req.DepartmentId
if departmentId > 0 {
db = db.Where("department_id = ?", departmentId)
}
givenName := strings.TrimSpace(req.GivenName)
if givenName != "" {
db = db.Where("given_name LIKE ?", fmt.Sprintf("%%%s%%", givenName))
}
status := req.Status status := req.Status
if status != 0 { if status != 0 {
db = db.Where("status = ?", status) db = db.Where("status = ?", status)
@ -55,6 +65,40 @@ func (s UserService) List(req *request.UserListReq) ([]*model.User, error) {
return list, err return list, err
} }
// ListCout 获取符合条件的数据列表条数
func (s UserService) ListCount(req *request.UserListReq) (int64, error) {
var count int64
db := common.DB.Model(&model.User{}).Order("id DESC")
username := strings.TrimSpace(req.Username)
if username != "" {
db = db.Where("username LIKE ?", fmt.Sprintf("%%%s%%", username))
}
nickname := strings.TrimSpace(req.Nickname)
if nickname != "" {
db = db.Where("nickname LIKE ?", fmt.Sprintf("%%%s%%", nickname))
}
mobile := strings.TrimSpace(req.Mobile)
if mobile != "" {
db = db.Where("mobile LIKE ?", fmt.Sprintf("%%%s%%", mobile))
}
departmentId := req.DepartmentId
if departmentId > 0 {
db = db.Where("department_id = ?", departmentId)
}
givenName := strings.TrimSpace(req.GivenName)
if givenName != "" {
db = db.Where("given_name LIKE ?", fmt.Sprintf("%%%s%%", givenName))
}
status := req.Status
if status != 0 {
db = db.Where("status = ?", status)
}
err := db.Count(&count).Error
return count, err
}
// List 获取数据列表 // List 获取数据列表
func (s UserService) ListAll() (list []*model.User, err error) { func (s UserService) ListAll() (list []*model.User, err error) {
err = common.DB.Model(&model.User{}).Order("created_at DESC").Find(&list).Error err = common.DB.Model(&model.User{}).Order("created_at DESC").Find(&list).Error

View File

@ -10,13 +10,17 @@ type GroupListReq struct {
// GroupAddReq 添加资源结构体 // GroupAddReq 添加资源结构体
type GroupAddReq struct { type GroupAddReq struct {
GroupType string `json:"groupType" validate:"required,min=1,max=20"`
GroupName string `json:"groupName" validate:"required,min=1,max=20"` GroupName string `json:"groupName" validate:"required,min=1,max=20"`
//父级Id 大于等于0 必填
ParentId uint `json:"parentId" validate:"omitempty,min=0"`
Remark string `json:"remark" validate:"min=0,max=100"` // 分组的中文描述 Remark string `json:"remark" validate:"min=0,max=100"` // 分组的中文描述
} }
// GroupUpdateReq 更新资源结构体 // GroupUpdateReq 更新资源结构体
type GroupUpdateReq struct { type GroupUpdateReq struct {
ID uint `json:"id" form:"id" validate:"required"` ID uint `json:"id" form:"id" validate:"required"`
GroupName string `json:"groupName" validate:"required,min=1,max=20"`
Remark string `json:"remark" validate:"min=0,max=100"` // 分组的中文描述 Remark string `json:"remark" validate:"min=0,max=100"` // 分组的中文描述
} }
@ -27,6 +31,10 @@ type GroupDeleteReq struct {
// GroupGetTreeReq 获取资源树结构体 // GroupGetTreeReq 获取资源树结构体
type GroupGetTreeReq struct { type GroupGetTreeReq struct {
GroupName string `json:"groupName" form:"groupName"`
Remark string `json:"remark" form:"remark"`
PageNum int `json:"pageNum" form:"pageNum"`
PageSize int `json:"pageSize" form:"pageSize"`
} }
type GroupAddUserReq struct { type GroupAddUserReq struct {

View File

@ -15,12 +15,15 @@ type UserAddReq struct {
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Introduction string `json:"introduction" validate:"min=0,max=255"` Introduction string `json:"introduction" validate:"min=0,max=255"`
Status uint `json:"status" validate:"oneof=1 2"` Status uint `json:"status" validate:"oneof=1 2"`
DepartmentId uint `json:"departmentId" validate:"required"`
Source string `json:"source" validate:"min=0,max=20"`
RoleIds []uint `json:"roleIds" validate:"required"` RoleIds []uint `json:"roleIds" validate:"required"`
} }
// UserUpdateReq 更新资源结构体 // UserUpdateReq 更新资源结构体
type UserUpdateReq struct { type UserUpdateReq struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
Username string `json:"username" validate:"required,min=2,max=20"`
Nickname string `json:"nickname" validate:"min=0,max=20"` Nickname string `json:"nickname" validate:"min=0,max=20"`
GivenName string `json:"givenName" validate:"min=0,max=20"` GivenName string `json:"givenName" validate:"min=0,max=20"`
Mail string `json:"mail" validate:"min=0,max=20"` Mail string `json:"mail" validate:"min=0,max=20"`
@ -31,6 +34,8 @@ type UserUpdateReq struct {
Mobile string `json:"mobile" validate:"checkMobile"` Mobile string `json:"mobile" validate:"checkMobile"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Introduction string `json:"introduction" validate:"min=0,max=255"` Introduction string `json:"introduction" validate:"min=0,max=255"`
DepartmentId uint `json:"departmentId" validate:"required"`
Source string `json:"source" validate:"min=0,max=20"`
RoleIds []uint `json:"roleIds" validate:"required"` RoleIds []uint `json:"roleIds" validate:"required"`
} }
@ -60,6 +65,8 @@ type UserListReq struct {
Username string `json:"username" form:"username"` Username string `json:"username" form:"username"`
Mobile string `json:"mobile" form:"mobile" ` Mobile string `json:"mobile" form:"mobile" `
Nickname string `json:"nickname" form:"nickname"` Nickname string `json:"nickname" form:"nickname"`
GivenName string `json:"givenName" form:"givenName"`
DepartmentId uint `json:"departmentId" form:"departmentId"`
Status uint `json:"status" form:"status" ` Status uint `json:"status" form:"status" `
PageNum int `json:"pageNum" form:"pageNum"` PageNum int `json:"pageNum" form:"pageNum"`
PageSize int `json:"pageSize" form:"pageSize"` PageSize int `json:"pageSize" form:"pageSize"`