diff --git a/src/bean/entity/system_preference_config.go b/src/bean/entity/system_preference_config.go new file mode 100644 index 0000000000000000000000000000000000000000..fa5ac3bd99fcde534538aff2e35527c61bf6e851 --- /dev/null +++ b/src/bean/entity/system_preference_config.go @@ -0,0 +1,37 @@ +package entity + +type SystemPreferenceConfig struct { + Id int64 `json:"id" xorm:"pk autoincr" vd:"$>0;msg:'请输入配置id'"` //id + // 授权配置 + SystemVersion string `json:"system_version"` //系统版本号 + SystemUser string `json:"system_user" vd:"len($)>0;msg:'请输入使用用户'"` //系统User + License string `json:"license" vd:"len($)>0;msg:'请输入license'"` //系统license + LicenseInformDay int `json:"license_inform_day"` //license剩余有效期提醒 + LicenseDeadDate string `json:"license_dead_date"` //license截至有效期 + // 登录页配置 + Logo string `json:"logo"` //logo + BgImage string `json:"bg_image"` //背景图 + BottomTitle string `json:"bottom_title"` //底部所有权内容 + Icp string `json:"icp"` //ICP备案 + IcpUrl string `json:"icp_url"` //ICP备案跳转链接 + Police string `json:"police"` //公安网备案 + PoliceUrl string `json:"police_url"` //公安网跳转链接 + LegalNotice string `json:"legal_notice"` //法律声明 + PrivacyPolicy string `json:"privacy_policy"` //隐私政策 + // 安全配置 + MinPwdLevel int `json:"min_pwd_level" vd:"$>0;msg:'请选择最低密码强度'"` //最低密码强度1弱2中3强 + ForceUpdateState int `json:"force_update_state"` //是否强制修改0否1是 + PwdValidity int `json:"pwd_validity" vd:"$ >= 0;msg:'请输入密码有效期'"` //密码有效期 + SessionValidity int `json:"session_validity" vd:"$ >= 10 && $ <= 30;msg:'会话有效期范围在(10~30分钟),请重新输入'"` //会话有效期 + AccessRuleState int `json:"access_rule_state" vd:"$== 0 || $==1;msg:'请选择系统访问规则模式'"` //访问规则模式0关闭1开启 + // 登录配置 + LoginConfigState int `json:"login_config_state" vd:"$== 0 || $==1;msg:'请选择是否启用登录配置'"` //是否启用登录配置0否1是 + LoginLimitTime int `json:"login_limit_time"` //登录受限时间 + LoginPwdError int `json:"login_pwd_error"` //密码错误次数 + LoginLockTime int `json:"login_lock_time"` //自动锁定时间 + +} + +type LicenseInfo struct { + License string `json:"license" vd:"len($)>0;msg:'请输入license'"` +} diff --git a/src/bean/vo/response/system_user.go b/src/bean/vo/response/system_user.go index 2d9541eabe05492a77130ca41b1af6ece8560f1f..802544bb8c90842256f86a79904dd38b35898a80 100644 --- a/src/bean/vo/response/system_user.go +++ b/src/bean/vo/response/system_user.go @@ -53,3 +53,40 @@ type SystemUserListRes struct { CreatedTime jsontime.Time `json:"created_time" ` // 创建时间 SystemId string `json:"system_id"` // 系统账号id } + +type CheckLicense struct { + SystemOptionsId int64 `json:"systemOptionsId"` + SystemVersion string `json:"systemVersion"` + SystemUser string `json:"systemUser"` + License string `json:"license"` + DeadDate string `json:"deadDate"` + CanUpdLicense int8 `json:"canUpdLicense"` + IsExpired int8 `json:"isExpired"` +} + +type LicenseInformAccess struct { + CanUpdLicense int8 `json:"canUpdLicense"` +} + +// 获取具有审批权限的角色 +type ApprovalAuthorityRole struct { + Id int64 `json:"id"` + RoleId string `json:"role_id"` // 角色id(uuid) + RoleName string `json:"role_name"` // 角色名称 +} + +// 获取具有审批权限的组织 +type ApprovalAuthorityOrg struct { + Id int64 `json:"id"` + OrganizationId string `json:"organization_id"` // 组织id(uuid) + OrganizationName string `json:"organization_name"` // 组织名称 +} + +// 获取具有审批权限的用户 +type ApprovalAuthorityUser struct { + Id int64 `json:"id"` + SystemId string `json:"system_id" xorm:"system_id"` // 系统账号id + RoleId string `json:"role_id"` // 角色id(uuid) + DataPurview int `json:"data_purview"` // 数据权限:1-仅自己,2-本组织所有,3-全平台所有 + OrganizationId string `json:"organization_id" xorm:"organization_id"` // 所属组织 +} diff --git a/src/common/client/redis.go b/src/common/client/redis.go index bb47d44ae54035b7cd80a8264945b80108dc23a0..9bd0e23afd36befe1ec3137180333024306ebe8f 100644 --- a/src/common/client/redis.go +++ b/src/common/client/redis.go @@ -17,6 +17,11 @@ import ( "go.uber.org/zap" ) +const ( + RULEKEY = "so-lock-rule" + LOCKKEY = "so-lock" +) + type Redis struct { Conn *redis.Client Prefix string @@ -97,3 +102,22 @@ func (r Redis) Expire(key string, expiration time.Duration) (bool, error) { key = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(key)) return r.Conn.Expire(strings.ToUpper(key), expiration).Result() } + +func (r Redis) Exist(key string) (int64, error) { + key = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(key)) + return r.Conn.Exists(strings.ToUpper(key)).Result() +} + +func (r Redis) Keys(pattern string) ([]string, error) { + pattern = fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(pattern)) + return r.Conn.Keys(strings.ToUpper(pattern)).Result() +} + +func (r Redis) DelList(keys []string) error { + fmt.Println("删除key为:", keys) + for k, v := range keys { + keys[k] = strings.ToUpper(fmt.Sprintf("%s-%s", strings.ToUpper(r.Prefix), strings.ToUpper(v))) + } + bmd := r.Conn.Del(keys...) + return bmd.Err() +} diff --git a/src/common/conf/options.go b/src/common/conf/options.go index 06e1a5671e1ace04fc7b18501f1cfad0be40a2ba..a788e8aabec102b5c3327c2dccee11ba636ad2df 100644 --- a/src/common/conf/options.go +++ b/src/common/conf/options.go @@ -31,7 +31,8 @@ type Config struct { MinioSecretKey string MinioBucket string //TempDirPrefix string - PrometheusHost string + PrometheusHost string + AccessRuleModeKey string } const ( diff --git a/src/controller/system_preference.go b/src/controller/system_preference.go new file mode 100644 index 0000000000000000000000000000000000000000..568d588c8400fe10f7a772b0cdd4cfddf0683fe8 --- /dev/null +++ b/src/controller/system_preference.go @@ -0,0 +1,143 @@ +package controller + +import ( + "errors" + vd "github.com/bytedance/go-tagexpr/validator" + "github.com/gin-gonic/gin" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/service" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" +) + +// 获取系统首选项信息 +func GetSystemOptions(c *gin.Context) { + svc := service.SystemOptionsSvc{} + if systemOptions, err := svc.GetSystemOptions(); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, nil, systemOptions) + } +} + +// 设置授权信息 +func SetLicenseOptions(c *gin.Context) { + var licenseOpts entity.SystemPreferenceConfig + if err := c.ShouldBindJSON(&licenseOpts); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if err := util.ValidateSimple(licenseOpts, "Id"); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + svc := service.SystemOptionsSvc{} + if err := svc.SetLicenseOptions(licenseOpts); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, nil) + } +} + +// 设置登陆页信息 +func SetLoginPageOptions(c *gin.Context) { + var licenseOpts entity.SystemPreferenceConfig + if err := c.ShouldBindJSON(&licenseOpts); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if err := util.ValidateSimple(licenseOpts, "Id"); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + svc := service.SystemOptionsSvc{} + if err := svc.SetLoginPageOptions(licenseOpts); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, nil) + } +} + +// 设置安全选项 +func SetSafeOptions(c *gin.Context) { + var safeOpts entity.SystemPreferenceConfig + if err := c.ShouldBindJSON(&safeOpts); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if err := util.ValidateSimple(safeOpts, "id", "force_update_state", "min_pwd_level", "session_validity", "pwd_validity", "access_rule_state"); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + svc := service.SystemOptionsSvc{} + if err := svc.SetSafeOptions(safeOpts); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, nil) + } +} + +// 设置登录配置 +func SetLoginOptions(c *gin.Context) { + var loginOpts entity.SystemPreferenceConfig + if err := c.ShouldBindJSON(&loginOpts); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if err := util.ValidateSimple(loginOpts, "id", "login_config_state"); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if loginOpts.LoginConfigState == 1 { + if loginOpts.LoginLimitTime == 0 || loginOpts.LoginPwdError == 0 || loginOpts.LoginLockTime == 0 { + SendJsonResponse(c, resp.InvalidParam.WithError(errors.New("登录受限参数不能为空")), nil) + return + } + } + svc := service.SystemOptionsSvc{} + if err := svc.SetLoginOptions(loginOpts); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, nil) + } +} + +// 校验license是否过期 +func CheckLicense(c *gin.Context) { + value, _ := c.Get("user_info") + user := value.(entity.SystemUserInfo) + svc := service.SystemOptionsSvc{} + if data, err := svc.CheckLicense(user); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, nil, data) + } +} + +// 一键还原受限 +func ReStartConfig(c *gin.Context) { + svc := service.SystemOptionsSvc{} + if data, err := svc.DeleteLockUser(); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, data) + } +} + +func GetLicenseInfo(c *gin.Context) { + license := entity.LicenseInfo{} + if err := c.ShouldBindJSON(&license); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), nil) + return + } + if err := vd.Validate(license); err != nil { + SendJsonResponse(c, resp.InvalidParam.WithError(err), "") + return + } + svc := service.SystemOptionsSvc{} + if data, err := svc.GetLicenseInfo(license); err != nil { + SendJsonResponse(c, err, nil) + } else { + SendJsonResponse(c, resp.OK, data) + } +} diff --git a/src/main.go b/src/main.go index a3d132fd05e2e640ec2469aa31557f15ccf7ecf3..b2851d1591e024f853f418a52b4915085d45cb67 100644 --- a/src/main.go +++ b/src/main.go @@ -71,7 +71,8 @@ func initConfig() { MinioSecretKey: util.SetEnvStr("MINIO_SECRET_KEY", "H76cPmwvH7vJ"), // Minio Secret MinioBucket: util.SetEnvStr("MINIO_BUCKET", "so-operation"), // Minio Bucket //TempDirPrefix: util.SetEnvStr("TEMP_DIR_PREFIX", "/app/xlsx/"), //模板目录前缀 - PrometheusHost: util.SetEnvStr("PROMETHEUS_HOST", "https://prometheus.wodcloud.com"), // Prometheus Host + PrometheusHost: util.SetEnvStr("PROMETHEUS_HOST", "https://prometheus.wodcloud.com"), // Prometheus Host + AccessRuleModeKey: "accessRuleMode", } } diff --git a/src/pkg/beagle/license/license.go b/src/pkg/beagle/license/license.go new file mode 100644 index 0000000000000000000000000000000000000000..79691287a0d6979767a90e6e7cec5c31f9f6186e --- /dev/null +++ b/src/pkg/beagle/license/license.go @@ -0,0 +1,137 @@ +/** + * @Author chenghao + * @Date 2021/12/7 + **/ + +package license + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "encoding/json" + "errors" + "fmt" +) + +type License struct { + ClusterID string `json:"clusterId"` + ClusterNode int `json:"clusterNode"` + TaskName string `json:"taskName"` + CustomerName string `json:"customerName"` + Version string `json:"version"` + DeadDate string `json:"deadDate"` // 授权码到期时间 + StartDate string `json:"startDate"` // 授权码开始时间 + ValidDate int `json:"validDate"` //有效期:单位月 +} + +func GetAesString(key interface{}) (res string, err error) { + jsonStr, _ := json.Marshal(key) + return EnPwdCode(jsonStr) +} + +/* +* +高级加密标准(Adevanced Encryption Standard ,AES) +16,24,32位字符串的话,分别对应AES-128,AES-192,AES-256 加密方法 +key不能泄露 +*/ +var PwdKey = []byte("BIGDATE@*#VCLOUDCLICENSE") + +// PKCS7 填充模式 +func PKCS7Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + //Repeat()函数的功能是把切片[]byte{byte(padding)}复制padding个,然后合并成新的字节切片返回 + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} + +// 填充的反向操作,删除填充字符串 +func PKCS7UnPadding(origData []byte) ([]byte, error) { + //获取数据长度 + length := len(origData) + if length == 0 { + return nil, errors.New("加密字符串错误!") + } else { + //获取填充字符串长度 + unpadding := int(origData[length-1]) + //截取切片,删除填充字节,并且返回明文 + return origData[:(length - unpadding)], nil + } +} + +// 实现加密 +func AesEcrypt(origData []byte, key []byte) ([]byte, error) { + //创建加密算法实例 + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + //获取块的大小 + blockSize := block.BlockSize() + //对数据进行填充,让数据长度满足需求 + origData = PKCS7Padding(origData, blockSize) + //采用AES加密方法中CBC加密模式 + blocMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + crypted := make([]byte, len(origData)) + //执行加密 + blocMode.CryptBlocks(crypted, origData) + return crypted, nil +} + +// 实现解密 +func AesDeCrypt(cypted []byte, key []byte) ([]byte, error) { + //创建加密算法实例 + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + //获取块大小 + blockSize := block.BlockSize() + //创建加密客户端实例 + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(cypted)) + //这个函数也可以用来解密 + blockMode.CryptBlocks(origData, cypted) + //去除填充字符串 + origData, err = PKCS7UnPadding(origData) + if err != nil { + return nil, err + } + return origData, err +} + +// 加密base64 +func EnPwdCode(pwd []byte) (string, error) { + result, err := AesEcrypt(pwd, PwdKey) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(result), err +} + +// 解密 +func DePwdCode(pwd string) ([]byte, error) { + //解密base64字符串 + pwdByte, err := base64.StdEncoding.DecodeString(pwd) + if err != nil { + return nil, err + } + //执行AES解密 + return AesDeCrypt(pwdByte, PwdKey) +} + +// DecodeLicense 解密授权码 +func DecodeLicense(secretKey string) (licenseInfo *License, err error) { + codeBytes, err := DePwdCode(secretKey) + if err != nil { + return nil, errors.New("授权码有误") + } + fmt.Println(string(codeBytes)) + err = json.Unmarshal(codeBytes, &licenseInfo) + if err != nil { + return nil, errors.New("秘钥解析失败") + } + return +} diff --git a/src/router/router.go b/src/router/router.go index c53046879abe4f82843abe45842e63d9151a465d..8853f88fcea44566573c98cf82962892e6103ae6 100644 --- a/src/router/router.go +++ b/src/router/router.go @@ -45,6 +45,8 @@ func Load(r *gin.Engine, middleware ...gin.HandlerFunc) { InitSystemMenuRouter(r) // 初始化字典相关路由(r) initDictRoute(r) + // 首选项配置相关路由 + InitPreferenceConfigRouter(r) // 初始化指标配置路由 InitMetricConfigRouter(r) // 初始化预警规则配置路由 diff --git a/src/router/systempreferencerouter.go b/src/router/systempreferencerouter.go new file mode 100644 index 0000000000000000000000000000000000000000..86a48cce64e8c8c313771acdf8436b34ee90293a --- /dev/null +++ b/src/router/systempreferencerouter.go @@ -0,0 +1,35 @@ +package router + +import ( + "fmt" + "github.com/gin-gonic/gin" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/controller" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/router/middleware/header" +) + +// 初始化配置管理路由 +func InitPreferenceConfigRouter(e *gin.Engine) { + sys := e.Group(fmt.Sprintf("%s/sysOptions", conf.Options.Prefix), header.SetContext) + { + sys.GET("", controller.GetSystemOptions) // 获取系统首选项信息 + sys.POST("licenseOpts", controller.SetLicenseOptions) // 设置授权信息 + sys.POST("loginPageOpts", controller.SetLoginPageOptions) // 设置授权信息 + sys.POST("safeOpts", controller.SetSafeOptions) // 设置安全选项 + sys.POST("loginOpts", controller.SetLoginOptions) // 设置登录选项 + sys.GET("checkLicense", controller.CheckLicense) // 校验license是否有效 + sys.POST("reStartConfig", controller.ReStartConfig) // 一键还原受限 + sys.POST("getLicenseInfo", controller.GetLicenseInfo) // 获取license信息 + } + + //rule := v5.Group("/accessRule", header.SetUserToContext) + //{ + // rule.POST("addAccessRule", controller.AddAccessRule, log.AddLogMiddleware("访问规则管理", "/addAccessRule", constant.OpTypeIntMap[constant.Add])) // 新增访问规则 + // rule.PUT("updateAccessRule", controller.UpdateAccessRule, log.AddLogMiddleware("访问规则管理", "/updateAccessRule", constant.OpTypeIntMap[constant.Edit])) // 编辑访问规则 + // rule.DELETE("delAccessRule", controller.DelAccessRule, log.AddLogMiddleware("访问规则管理", "/delAccessRule", constant.OpTypeIntMap[constant.AllDelete])) // 删除访问规则 + // rule.GET("listAccessRule", controller.ListAccessRule, log.AddLogMiddleware("访问规则管理", "/listAccessRule", constant.OpTypeIntMap[constant.Find])) // 查询访问规则列表 + // rule.GET("listRuleUser", controller.ListRuleUser) // 查询用户详情列表 + // rule.PUT("updateState", controller.UpdateState) // 修改规则状态 + //} + +} diff --git a/src/service/login.go b/src/service/login.go index 19ae65d2f6df0d798f82188fab7ec37c88b6a37e..9770fa9f9609452408741ef19141df6d98f26ee3 100644 --- a/src/service/login.go +++ b/src/service/login.go @@ -239,3 +239,73 @@ func (u *UserSvc) VerifyCaptcha(id, value string) (err error) { } return } + +// 用户锁定(此逻辑包含锁定规则以及锁定key) +func (u *UserSvc) UserLock(systemId, pwd string, config *entity.SystemPreferenceConfig) (err error) { + r, err := client.GetRedisClient() + if err != nil { + return resp.RedisConnectError.ErrorDetail(err) + } + startLockKey := fmt.Sprintf("%s-%s", client.RULEKEY, systemId) + lockSystemKey := fmt.Sprintf("%s-%s", client.LOCKKEY, systemId) + errorNum, err := r.Get(startLockKey) + if err != nil && err != redis.Nil { + conf.Logger.Error("获取密码插入次数失败", zap.Error(err)) + return resp.RedisExecError.ErrorDetail(err) + } else if errorNum == "" { + if err := r.Set(startLockKey, 0, time.Duration(config.LoginLimitTime)*time.Hour); err != nil { + conf.Logger.Error("密码插入次数插入redis失败", zap.Error(err)) + return resp.RedisExecError.ErrorDetail(err) + } + } else if cast.ToInt(errorNum) >= config.LoginPwdError { + // 错误上限, 自动锁定 + has, err := r.Exist(lockSystemKey) + if err != nil { + return err + } + if has == 1 { + // 获取锁定规则过期时间 + ttl, err := r.Ttl(startLockKey) + if err != nil { + conf.Logger.Error("获取锁定规则过期时间失败", zap.Error(err)) + return resp.RedisExecError.ErrorDetail(err) + } + return resp.FAIL.ErrorDetail(fmt.Errorf("您已经%s次输错密码,账号已锁定,请在%s分后重试!", errorNum, ttl/60)) + } + // 如果不存在过期时间key,则代表锁定时间已过期,删除锁定规则,重新进行新的锁定规则 + err = r.Del(startLockKey) + if err != nil { + conf.Logger.Error("删除锁定规则文件失败", zap.Error(err)) + return resp.RedisExecError.ErrorDetail(err) + } + } + // 判断是否输错密码 + h := md5.New() + _, err = h.Write([]byte(strings.ToUpper(systemId + "-" + u.PassWord))) + if err != nil { + conf.Logger.Error("加密错误", zap.Error(err)) + return err + } + upperMd5Pass := strings.ToUpper(hex.EncodeToString(h.Sum(nil))) + if upperMd5Pass != pwd { + incr, err := r.Incr(startLockKey) + if err != nil { + conf.Logger.Error("写入错误次数失败", zap.Error(err)) + return err + } + conf.Logger.Info("当前错误次数为", zap.Int64("incr", incr)) + if int(incr) >= config.LoginPwdError { + err = r.Set(lockSystemKey, 0, time.Duration(config.LoginLockTime)*time.Minute) + if err != nil { + return err + } + } + return fmt.Errorf("请正确输入密码,连续%d次输错密码,账号将被锁定,还可重试%d次", incr, config.LoginPwdError-cast.ToInt(incr)) + } + // 密码正确 删除 锁定文件 + if err := r.Del(startLockKey); err != nil { + conf.Logger.Error("删除锁定文件错误", zap.Error(err)) + return err + } + return nil +} diff --git a/src/service/system_preference.go b/src/service/system_preference.go new file mode 100644 index 0000000000000000000000000000000000000000..884e216fecced11f9df64b82887b26847cd6c43b --- /dev/null +++ b/src/service/system_preference.go @@ -0,0 +1,195 @@ +package service + +import ( + "errors" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/license" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" + "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" + + "time" + + "go.uber.org/zap" +) + +type SystemOptionsSvc struct { +} + +// 获取系统首选项 +func (so *SystemOptionsSvc) GetSystemOptions() (config *entity.SystemPreferenceConfig, err error) { + db, err := client.GetDbClient() + if err != nil { + return nil, resp.DbConnectError.WithError(err) + } + var systemOpts entity.SystemPreferenceConfig + if _, err := db.Table("system_preference_config").Get(&systemOpts); err != nil { + conf.Logger.Error("获取系统首选项配置失败", zap.Error(err)) + return nil, resp.DbSelectError.WithError(err) + } + return &systemOpts, nil +} + +func (so *SystemOptionsSvc) SetLicenseOptions(licenseOpts entity.SystemPreferenceConfig) error { + if err := util.ValidateSimple(licenseOpts, "Id"); err != nil { + return resp.InvalidParam.WithError(err) + } + // 验证license信息 + if licenseInfo, err := license.DecodeLicense(licenseOpts.License); err != nil { + conf.Logger.Error("license err", zap.Error(err)) + return resp.InvalidParam.WithError(err) + } else { + conf.Logger.Info("license解析信息:", zap.Any("license", licenseInfo)) + if licenseInfo.CustomerName != licenseOpts.SystemUser { + return resp.InvalidParam.WithError(err) + } + if len(licenseInfo.DeadDate) != 11 { + deadDate, _ := time.Parse("2006-01-02", licenseInfo.DeadDate) + // It is shorthand for t.Sub(time.Now()). + if time.Until(deadDate) <= 0 { + return resp.InvalidParam.WithError(errors.New("license 已过期")) + } + licenseOpts.LicenseDeadDate = licenseInfo.DeadDate + } + } + db, err := client.GetDbClient() + if err != nil { + return resp.DbConnectError.ErrorDetail(err) + } + if _, err := db.Table("system_preference_config").Where("id = ?", licenseOpts.Id).Cols("system_version", "system_user", "license", "license_dead_date", "license_inform_day").Update(licenseOpts); err != nil { + conf.Logger.Error("dberr", zap.Error(err)) + return resp.DbUpdateError.ErrorDetail(err) + } + return nil +} + +// 设置登录页 +func (so *SystemOptionsSvc) SetLoginPageOptions(safeOpts entity.SystemPreferenceConfig) error { + + // 获取数据库连接 + db, err := client.GetDbClient() + if err != nil { + return resp.DbConnectError.ErrorDetail(err) + } + _, err = db.Table("system_preference_config").Where("id = ?", safeOpts.Id). + Cols("logo", "bg_image", "bottom_title", "icp", "icp_url", "police", "police_url", "legal_notice", "privacy_policy"). + Update(safeOpts) + if err != nil { + conf.Logger.Error("设置登录页信息失败!", zap.Error(err)) + return resp.DbUpdateError.ErrorDetail(err) + } + + return nil +} + +// 设置安全信息 +func (so *SystemOptionsSvc) SetSafeOptions(safeOpts entity.SystemPreferenceConfig) error { + rCon, err := client.GetRedisClient() + if err != nil { + return resp.RedisConnectError.ErrorDetail(err) + } + // 获取数据库连接 + db, err := client.GetDbClient() + if err != nil { + return resp.DbConnectError.ErrorDetail(err) + } + _, err = db.Table("system_preference_config").Where("id = ?", safeOpts.Id). + Cols("min_pwd_level", "force_update_state", "pwd_validity", "session_validity", "access_rule_state"). + Update(safeOpts) + if err != nil { + conf.Logger.Error("设置安全信息失败!", zap.Error(err)) + return resp.DbUpdateError.ErrorDetail(err) + } + go func() { + rCon.Del(conf.Options.AccessRuleModeKey) + }() + return nil +} + +// 设置登录配置 +func (so *SystemOptionsSvc) SetLoginOptions(loginOpts entity.SystemPreferenceConfig) error { + // 获取数据库连接 + db, err := client.GetDbClient() + if err != nil { + return resp.DbConnectError.ErrorDetail(err) + } + _, err = db.Table("system_preference_config"). + Cols("login_config_state", "login_limit_time", "login_pwd_error", "login_lock_time"). + Where("id = ?", loginOpts.Id).Update(loginOpts) + if err != nil { + conf.Logger.Error("更新登录配置信息失败", zap.Error(err)) + return resp.DbUpdateError.ErrorDetail(err) + } + // 若有账号在锁定当中,管理员将该配置设置为失效,则所有锁定的账号解锁 + go func() { + if loginOpts.LoginConfigState == 0 { + so.DeleteLockUser() + } + }() + return nil +} + +// 校验License是否过期 +func (so *SystemOptionsSvc) CheckLicense(user entity.SystemUserInfo) (*response.CheckLicense, error) { + systemOptions, e := so.GetSystemOptions() + if e != nil { + return nil, e + } + var checkLicense = response.CheckLicense{ + SystemOptionsId: systemOptions.Id, + SystemVersion: systemOptions.SystemVersion, + SystemUser: systemOptions.SystemUser, + License: systemOptions.License, + CanUpdLicense: 2, + IsExpired: 3, + DeadDate: systemOptions.LicenseDeadDate, + } + //if user.SystemId == util.AdminSystemUser { + // checkLicense.CanUpdLicense = 1 + //} + if len(checkLicense.DeadDate) == 10 { + deadDate, _ := time.Parse("2006-01-02", checkLicense.DeadDate) + // It is shorthand for t.Sub(time.Now()). + leftDays := time.Until(deadDate).Hours() / 24 + if leftDays <= 0 { + checkLicense.IsExpired = 1 + } else if int(leftDays) <= systemOptions.LicenseInformDay { + checkLicense.IsExpired = 2 + } + } + return &checkLicense, nil +} + +// 所有锁定的账号解锁 +func (so *SystemOptionsSvc) DeleteLockUser() (data interface{}, err error) { + rCon, err := client.GetRedisClient() + if err != nil { + return nil, resp.RedisConnectError.ErrorDetail(err) + } + ruleKeys, err := rCon.Keys(client.RULEKEY) + if err != nil { + conf.Logger.Error("查询锁定规则key序列失败") + return nil, resp.RedisExecError.ErrorDetail(err) + } + lockKeys, err := rCon.Keys(client.LOCKKEY) + if err != nil { + conf.Logger.Error("查询锁定key序列失败") + return nil, resp.RedisExecError.ErrorDetail(err) + } + lockKeys = append(lockKeys, ruleKeys...) + if len(lockKeys) == 0 { + return "无账号锁定", nil + } + err = rCon.DelList(lockKeys) + if err != nil { + conf.Logger.Error("批量删除锁定key失败") + return "", resp.RedisExecError.ErrorDetail(err) + } + return "", nil +} + +func (so *SystemOptionsSvc) GetLicenseInfo(str entity.LicenseInfo) (data interface{}, err error) { + return license.DecodeLicense(str.License) +}