From cc663e692b85ea9bad19170d705b4139c4ba3835 Mon Sep 17 00:00:00 2001 From: kiven0201 Date: Mon, 3 Jul 2023 17:40:06 +0800 Subject: [PATCH 1/4] =?UTF-8?q?1=E3=80=81=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=AF=86=E7=A0=81=E9=9A=8F=E6=97=B6=E4=BA=A7=E7=94=9F?= =?UTF-8?q?8=E4=BD=8D,=E7=94=B1=E5=A4=A7=E5=B0=8F=E5=86=99=E5=AD=97?= =?UTF-8?q?=E6=AF=8D=E3=80=81=E6=95=B0=E5=AD=97=E3=80=81=E7=89=B9=E6=AE=8A?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E3=80=82=202=E3=80=81=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E6=97=B6=E6=96=B0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=E5=92=8C=E5=AF=86=E7=A0=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E5=8F=91=E9=80=81=E7=BB=99=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yml | 2 +- logic/user_logic.go | 13 ++++++++++++- public/tools/email.go | 18 ++++++++++++++++++ public/tools/randpass.go | 23 +++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 public/tools/randpass.go diff --git a/config.yml b/config.yml index 6856b74..1168711 100644 --- a/config.yml +++ b/config.yml @@ -102,7 +102,7 @@ ldap: # ldap用户OU user-dn: "ou=people,dc=eryajf,dc=net" # ldap用户初始默认密码 - user-init-password: "123456" + # user-init-password: "123456" # 是否允许更改分组DN group-name-modify: false # 是否允许更改用户DN diff --git a/logic/user_logic.go b/logic/user_logic.go index 1fcdea5..72a0e6b 100644 --- a/logic/user_logic.go +++ b/logic/user_logic.go @@ -50,7 +50,10 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr return nil, tools.NewValidatorError(fmt.Errorf("密码长度至少为6位")) } } else { - r.Password = config.Conf.Ldap.UserInitPassword + //取消默认密码为123456 改为随时生成8位 + //r.Password = config.Conf.Ldap.UserInitPassword + r.Password = tools.GenerateRandomPassword() + } // 当前登陆用户角色排序最小值(最高等级角色)以及当前登陆的用户 @@ -118,6 +121,14 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr if err != nil { return nil, tools.NewOperationError(fmt.Errorf("添加用户失败" + err.Error())) } + + + //用户注册成功后通过邮件发送 + err = tools.SendResigtryMail([]string{user.Mail}, user.Username, r.Password) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("注册用户邮件发送失败" + err.Error())) + } + return nil, nil } diff --git a/public/tools/email.go b/public/tools/email.go index ed9d90d..c63ef0e 100644 --- a/public/tools/email.go +++ b/public/tools/email.go @@ -36,6 +36,24 @@ func email(mailTo []string, subject string, body string) error { return do.DialAndSend(newmail) } +func SendResigtryMail(sendto []string, user string, passwd string) error { + subject := "新注册用户" + body := fmt.Sprintf(`
+
+ 尊敬的用户 %s ,您好! +
+
+

你注册的用户密码为: %s ,为了保证账号安全,切勿向他人泄露,感谢您的理解与使用。

+
+
+

此邮箱为系统邮箱,请勿回复。

+
+
`, user, passwd) + return email(sendto, subject, body) + +} + + func SendMail(sendto []string, pass string) error { subject := "重置LDAP密码成功" // 邮件正文 diff --git a/public/tools/randpass.go b/public/tools/randpass.go new file mode 100644 index 0000000..38b7f3f --- /dev/null +++ b/public/tools/randpass.go @@ -0,0 +1,23 @@ +package tools + +import ( + "crypto/rand" + "math/big" +) + +const ( + passwordLength = 8 + letters = "abcdefghijklmnopqrstu@vwxyzABCDEFGHIJKL#MNOP*QRSTUVWXYZ0123456789" + lettersLength = len(letters) +) + +func GenerateRandomPassword() string { + password := make([]byte, passwordLength) + + for i := range password { + index, _ := rand.Int(rand.Reader, big.NewInt(int64(lettersLength))) + password[i] = letters[index.Int64()] + } + + return string(password) +} From fb14e5713c5bc64c5efde5ed7f8dfda9b944285a Mon Sep 17 00:00:00 2001 From: kiven0201 Date: Tue, 4 Jul 2023 10:32:42 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E9=BB=98=E8=AE=A48=E4=BD=8D=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E4=BF=AE=E6=94=B9=E4=B8=BA16=E4=BD=8D=E9=9A=8F?= =?UTF-8?q?=E6=9C=BA=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/ildap/user_ildap.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/service/ildap/user_ildap.go b/service/ildap/user_ildap.go index 8b3683a..efa7527 100644 --- a/service/ildap/user_ildap.go +++ b/service/ildap/user_ildap.go @@ -143,7 +143,9 @@ func (x UserService) NewPwd(username string) (string, error) { if username == "admin" { udn = config.Conf.Ldap.AdminDN } - modifyPass := ldap.NewPasswordModifyRequest(udn, "", "") + //默认产生8位随机密码,不符合公司规范,故生成16位随机密码 + newPasswd := tools.GenerateRandomPassword() + modifyPass := ldap.NewPasswordModifyRequest(udn, "", newPasswd) // 获取 LDAP 连接 conn, err := common.GetLDAPConn() @@ -156,6 +158,8 @@ func (x UserService) NewPwd(username string) (string, error) { if err != nil { return "", fmt.Errorf("password modify failed for %s, err: %v", username, err) } + + newpass.GeneratedPassword = newPasswd return newpass.GeneratedPassword, nil } func (x UserService) ListUserDN() (users []*model.User, err error) { From 266da34413ea07b8951867f2aacbeb89c1d14eab Mon Sep 17 00:00:00 2001 From: kiven0201 Date: Wed, 5 Jul 2023 17:04:55 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logic/user_logic.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/logic/user_logic.go b/logic/user_logic.go index 72a0e6b..84e7242 100644 --- a/logic/user_logic.go +++ b/logic/user_logic.go @@ -411,6 +411,25 @@ func (l UserLogic) ChangeUserStatus(c *gin.Context, req interface{}) (data inter if err != nil { return nil, tools.NewLdapError(fmt.Errorf("在LDAP添加用户失败" + err.Error())) } + + //修复,当用户状态为离职时,定时任务会将ldap用户同步sql,发现用户已离职并且ldap已删除,会将sql中同步状态设置为2 + //再次将用户状态设置为在职时,信息会再次从sql同步至ldap,但同步状态依然为2,点击同步时会提示用户已存在。 + //将离职人员再次设置为在职时,先查询用户是否被正常创建,如创建成功则将同步状态修改为1(已同步) + filter := map[string]interface{}{ + "uid": user.Username, + } + exist, err := ildap.User.Exist(filter) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("查询用户失败" + err.Error())) + } + + if exist { + isql.User.ChangeSyncState(int(r.ID), 1) + } else { + return nil, tools.NewLdapError(fmt.Errorf("用户不存在" + err.Error())) + } + + } err = isql.User.ChangeStatus(int(r.ID), int(r.Status)) if err != nil { From 4ae6732ae3822e902ada5bc48ca961b8dc9e6ff6 Mon Sep 17 00:00:00 2001 From: chenjun61 Date: Fri, 7 Jul 2023 16:57:25 +0800 Subject: [PATCH 4/4] =?UTF-8?q?1=E3=80=81=E5=A2=9E=E5=8A=A0=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E7=94=A8=E6=88=B7=E6=9B=B4=E6=96=B0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=20=EF=BC=8C=E8=B6=85=E8=BF=8775=E5=A4=A9=E6=AF=8F=E5=A4=A9?= =?UTF-8?q?=E9=82=AE=E4=BB=B6=E6=8F=90=E9=86=92=E4=BF=AE=E6=94=B9=E5=AF=86?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E8=B6=8590=E5=A4=A9=E5=90=8E=E7=A6=81?= =?UTF-8?q?=E7=94=A8=E8=B4=A6=E6=88=B7,=E6=AF=8F=E5=A4=A9=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E4=B8=80=E6=AC=A1;=202=E3=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.yml | 3 ++ config/config.go | 18 +++++----- logic/a_logic.go | 9 +++++ logic/user_logic.go | 79 +++++++++++++++++++++++++++++++++---------- public/tools/email.go | 17 ++++++++++ public/tools/time.go | 17 ++++++++++ 6 files changed, 118 insertions(+), 25 deletions(-) create mode 100644 public/tools/time.go diff --git a/config.yml b/config.yml index 1168711..7f63d3b 100644 --- a/config.yml +++ b/config.yml @@ -12,6 +12,9 @@ system: rsa-public-key: go-ldap-admin-pub.pem # rsa私钥文件路径(config.yml相对路径, 也可以填绝对路径) rsa-private-key: go-ldap-admin-priv.pem +#定时扫描用户过期时间 90天自动将状态修改为不可用 + enable-check-user: true + check-user-sync-time: "0 */1 * * * *" logs: # 日志等级(-1:Debug, 0:Info, 1:Warn, 2:Error, 3:DPanic, 4:Panic, 5:Fatal, -1<=level<=5, 参照zap.level源码) diff --git a/config/config.go b/config/config.go index 2f10749..5c07194 100644 --- a/config/config.go +++ b/config/config.go @@ -86,14 +86,16 @@ func RSAReadKeyFromFile(filename string) []byte { } type SystemConfig struct { - Mode string `mapstructure:"mode" json:"mode"` - UrlPathPrefix string `mapstructure:"url-path-prefix" json:"urlPathPrefix"` - Port int `mapstructure:"port" json:"port"` - InitData bool `mapstructure:"init-data" json:"initData"` - RSAPublicKey string `mapstructure:"rsa-public-key" json:"rsaPublicKey"` - RSAPrivateKey string `mapstructure:"rsa-private-key" json:"rsaPrivateKey"` - RSAPublicBytes []byte `mapstructure:"-" json:"-"` - RSAPrivateBytes []byte `mapstructure:"-" json:"-"` + Mode string `mapstructure:"mode" json:"mode"` + UrlPathPrefix string `mapstructure:"url-path-prefix" json:"urlPathPrefix"` + Port int `mapstructure:"port" json:"port"` + InitData bool `mapstructure:"init-data" json:"initData"` + RSAPublicKey string `mapstructure:"rsa-public-key" json:"rsaPublicKey"` + RSAPrivateKey string `mapstructure:"rsa-private-key" json:"rsaPrivateKey"` + RSAPublicBytes []byte `mapstructure:"-" json:"-"` + RSAPrivateBytes []byte `mapstructure:"-" json:"-"` + EnableCheckUser bool `mapstructure:"enable-check-user" json:"enableCheckUser"` + CheckUserSyncTime string `mapstructure:"check-user-sync-time" json:"checkUserSyncTime"` } type LogsConfig struct { diff --git a/logic/a_logic.go b/logic/a_logic.go index 87f1ba6..ddf782a 100644 --- a/logic/a_logic.go +++ b/logic/a_logic.go @@ -330,6 +330,15 @@ func ConvertUserData(flag string, remoteData []map[string]interface{}) (users [] func InitCron() { c := cron.New(cron.WithSeconds()) + if config.Conf.System.EnableCheckUser { + _, err := c.AddFunc(config.Conf.System.CheckUserSyncTime, func() { + User.CheckUserTime(nil, nil) + }) + if err != nil { + common.Log.Errorf("eteststset: %v", err) + } + } + if config.Conf.DingTalk.EnableSync { //启动定时任务 _, err := c.AddFunc(config.Conf.DingTalk.DeptSyncTime, func() { diff --git a/logic/user_logic.go b/logic/user_logic.go index 84e7242..97d9d7b 100644 --- a/logic/user_logic.go +++ b/logic/user_logic.go @@ -2,6 +2,7 @@ package logic import ( "fmt" + "time" "github.com/eryajf/go-ldap-admin/config" "github.com/eryajf/go-ldap-admin/model" @@ -122,7 +123,6 @@ func (l UserLogic) Add(c *gin.Context, req interface{}) (data interface{}, rspEr return nil, tools.NewOperationError(fmt.Errorf("添加用户失败" + err.Error())) } - //用户注册成功后通过邮件发送 err = tools.SendResigtryMail([]string{user.Mail}, user.Username, r.Password) if err != nil { @@ -412,23 +412,22 @@ func (l UserLogic) ChangeUserStatus(c *gin.Context, req interface{}) (data inter return nil, tools.NewLdapError(fmt.Errorf("在LDAP添加用户失败" + err.Error())) } - //修复,当用户状态为离职时,定时任务会将ldap用户同步sql,发现用户已离职并且ldap已删除,会将sql中同步状态设置为2 - //再次将用户状态设置为在职时,信息会再次从sql同步至ldap,但同步状态依然为2,点击同步时会提示用户已存在。 - //将离职人员再次设置为在职时,先查询用户是否被正常创建,如创建成功则将同步状态修改为1(已同步) - filter := map[string]interface{}{ - "uid": user.Username, - } - exist, err := ildap.User.Exist(filter) - if err != nil { - return nil, tools.NewLdapError(fmt.Errorf("查询用户失败" + err.Error())) - } - - if exist { - isql.User.ChangeSyncState(int(r.ID), 1) - } else { - return nil, tools.NewLdapError(fmt.Errorf("用户不存在" + err.Error())) - } + //修复,当用户状态为离职时,定时任务会将ldap用户同步sql,发现用户已离职并且ldap已删除,会将sql中同步状态设置为2 + //再次将用户状态设置为在职时,信息会再次从sql同步至ldap,但同步状态依然为2,点击同步时会提示用户已存在。 + //将离职人员再次设置为在职时,先查询用户是否被正常创建,如创建成功则将同步状态修改为1(已同步) + filter := map[string]interface{}{ + "uid": user.Username, + } + exist, err := ildap.User.Exist(filter) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("查询用户失败" + err.Error())) + } + if exist { + isql.User.ChangeSyncState(int(r.ID), 1) + } else { + return nil, tools.NewLdapError(fmt.Errorf("用户不存在" + err.Error())) + } } err = isql.User.ChangeStatus(int(r.ID), int(r.Status)) @@ -454,3 +453,49 @@ func (l UserLogic) GetUserInfo(c *gin.Context, req interface{}) (data interface{ } return user, nil } + +// 检查用户时间,超过90天禁用账号。提前15天邮件通知 +func (l UserLogic) CheckUserTime(c *gin.Context, req interface{}) (data interface{}, rspError interface{}) { + + //获取所有用户信息 + users, err := isql.User.ListAll() + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("获取用户列表失败:" + err.Error())) + } + + for _, user := range users { + //当前时间 + currentTime := time.Now() + //用户更新时间转换成时间戳 + updatetimestamp := tools.ConvertToTimestamp(user.UpdatedAt) + //当前时间转换成时间戳 + nowtimestamp := tools.ConvertToTimestamp(currentTime) + //计算更新时间与当前时间差值,大于75邮件通知 + days := tools.CalculateDaysBetweenTimestamps(updatetimestamp, nowtimestamp) + //排除dev-jeknis-git、admin两个用户的检查 + if user.Username != "dev-jenkins-git" && user.Username != "admin" && user.Status != 2 { + if days >= 75 { + err = tools.SendCheckUserMail([]string{user.Mail}, user.Username, int(days)) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("检查用户密码过期邮件发送失败" + err.Error())) + } + } + + //大于91天则将账号设置为离职状态 + if days > 91 { + //修改用户状态为2,离职 + err = isql.User.ChangeStatus(int(user.ID), 2) + if err != nil { + return nil, tools.NewMySqlError(fmt.Errorf("获取用户列表失败:" + err.Error())) + } + //并同步删除ldap用户信息 + err = ildap.User.Delete(user.UserDN) + if err != nil { + return nil, tools.NewLdapError(fmt.Errorf("在LDAP删除用户失败" + err.Error())) + } + } + } + + } + return nil, nil +} diff --git a/public/tools/email.go b/public/tools/email.go index c63ef0e..09912d1 100644 --- a/public/tools/email.go +++ b/public/tools/email.go @@ -53,6 +53,23 @@ func SendResigtryMail(sendto []string, user string, passwd string) error { } +// 发送检查用户更新密码时间 +func SendCheckUserMail(sendto []string, user string, day int) error { + subject := "用户过期" + body := fmt.Sprintf(`
+
+ 尊敬的用户: %s ,您好! +
+
+

你的用户密码将于 %d 后到期, 为不影响正常使用,请及时修改密码。

+
+
+

此邮箱为系统邮箱,请勿回复。

+
+
`, user, day) + return email(sendto, subject, body) + +} func SendMail(sendto []string, pass string) error { subject := "重置LDAP密码成功" diff --git a/public/tools/time.go b/public/tools/time.go new file mode 100644 index 0000000..2d6cfa7 --- /dev/null +++ b/public/tools/time.go @@ -0,0 +1,17 @@ +package tools + +import "time" + +func ConvertToTimestamp(t time.Time) int64 { + timestamp := t.Unix() + return timestamp +} + +func CalculateDaysBetweenTimestamps(timestamp1, timestamp2 int64) int { + t1 := time.Unix(timestamp1, 0) + t2 := time.Unix(timestamp2, 0) + + duration := t2.Sub(t1) + days := int(duration.Hours() / 24) + return days +}