434 lines
21 KiB
Go
434 lines
21 KiB
Go
package sys
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
|
|
"hotgo/internal/consts"
|
|
"hotgo/internal/dao"
|
|
"hotgo/internal/library/contexts"
|
|
"hotgo/internal/library/imtenant"
|
|
"hotgo/internal/model/input/sysin"
|
|
"hotgo/internal/service"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
)
|
|
|
|
type sSysImSecurityPolicy struct{}
|
|
|
|
func NewSysImSecurityPolicy() *sSysImSecurityPolicy { return &sSysImSecurityPolicy{} }
|
|
func init() { service.RegisterSysImSecurityPolicy(NewSysImSecurityPolicy()) }
|
|
|
|
func securityPolicyFields(alias string) []any {
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
p := alias
|
|
if p != "" {
|
|
p += "."
|
|
}
|
|
return []any{p + c.Id, p + c.TenantId, p + c.PolicyVersion, p + c.PolicyName, p + c.PublishStatus, "CAST(" + p + c.PasswordPolicy + " AS CHAR) password_policy", "CAST(" + p + c.LoginRateLimit + " AS CHAR) login_rate_limit", "CAST(" + p + c.SensitiveWordPolicy + " AS CHAR) sensitive_word_policy", "CAST(" + p + c.FileReviewPolicy + " AS CHAR) file_review_policy", "CAST(" + p + c.RealnamePolicy + " AS CHAR) realname_policy", "CAST(" + p + c.RiskChallengePolicy + " AS CHAR) risk_challenge_policy", p + c.GlobalFriendEnabled, "CAST(" + p + c.FriendDisabledPolicy + " AS CHAR) friend_disabled_policy", p + c.MessageCurfewEnabled, p + c.Remark, p + c.PublishedAt, p + c.PublishedBy, p + c.CreatedAt, p + c.UpdatedAt}
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) List(ctx context.Context, in *sysin.ImSecurityPolicyListInp) (list []*sysin.ImSecurityPolicyModel, total int, err error) {
|
|
if err = in.Filter(ctx); err != nil {
|
|
return
|
|
}
|
|
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
model := imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Fields(securityPolicyFields("")...).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt)
|
|
if in.PublishStatus != "" {
|
|
model = model.Where(c.PublishStatus, in.PublishStatus)
|
|
}
|
|
err = model.Page(in.Page, in.PerPage).OrderDesc(c.PolicyVersion).ScanAndCount(&list, &total, false)
|
|
return
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) View(ctx context.Context, in *sysin.ImSecurityPolicyViewInp) (res *sysin.ImSecurityPolicyModel, err error) {
|
|
if err = in.Filter(ctx); err != nil {
|
|
return
|
|
}
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
baseModel := imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Fields(securityPolicyFields("")...).WhereNull(c.DeletedAt)
|
|
if in.Id > 0 {
|
|
err = baseModel.Where(c.Id, in.Id).Scan(&res)
|
|
} else {
|
|
tenantID, _, resolveErr := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if resolveErr != nil {
|
|
return nil, resolveErr
|
|
}
|
|
err = baseModel.Where(c.TenantId, tenantID).
|
|
Where(c.PublishStatus, consts.ImSecurityPublishPublished).
|
|
OrderDesc(c.PolicyVersion).Scan(&res)
|
|
if err == nil && res == nil {
|
|
err = baseModel.Where(c.TenantId, tenantID).OrderDesc(c.PolicyVersion).Scan(&res)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "获取安全策略失败")
|
|
}
|
|
if res == nil {
|
|
return nil, gerror.New("安全策略不存在")
|
|
}
|
|
if _, _, err = resolveImPolicyTenant(ctx, res.TenantId); err != nil {
|
|
return nil, err
|
|
}
|
|
res.CurfewRules, err = s.curfewRules(ctx, res.Id)
|
|
return
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) curfewRules(ctx context.Context, policyID int64) (list []*sysin.ImMessageCurfewRuleModel, err error) {
|
|
c := dao.ImMessageCurfewRules.Columns()
|
|
err = imtenant.RegistryDB().Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Fields(c.Id, c.RuleCode, c.RuleName, c.DateType, "CAST("+c.CustomDates+" AS CHAR) custom_dates", "CAST("+c.Weekdays+" AS CHAR) weekdays", c.Timezone, "TIME_FORMAT("+c.StartTime+",'%H:%i') start_time", "TIME_FORMAT("+c.EndTime+",'%H:%i') end_time", c.Scope, "CAST("+c.WhitelistUserIds+" AS CHAR) whitelist_user_ids", "CAST("+c.WhitelistGroupIds+" AS CHAR) whitelist_group_ids", c.Message, c.Status, c.Sort).Where(c.PolicyId, policyID).WhereNull(c.DeletedAt).OrderAsc(c.Sort).OrderAsc(c.Id).Scan(&list)
|
|
return
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) Save(ctx context.Context, in *sysin.ImSecurityPolicySaveInp) (id int64, err error) {
|
|
if err = in.Filter(ctx); err != nil {
|
|
return
|
|
}
|
|
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
db := imtenant.RegistryDB()
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
rc := dao.ImMessageCurfewRules.Columns()
|
|
operator := contexts.GetUserId(ctx)
|
|
now := gtime.Now()
|
|
err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
version := uint(0)
|
|
if in.Id > 0 {
|
|
current, readErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One()
|
|
if readErr != nil {
|
|
return readErr
|
|
}
|
|
if current.IsEmpty() || current[c.TenantId].Int64() != tenantID {
|
|
return gerror.New("安全策略不存在")
|
|
}
|
|
if current[c.PublishStatus].String() != consts.ImSecurityPublishDraft {
|
|
return gerror.New("只有草稿状态的安全策略可以编辑")
|
|
}
|
|
version = current[c.PolicyVersion].Uint()
|
|
id = in.Id
|
|
} else {
|
|
maxValue, maxErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.TenantId, tenantID).Max(c.PolicyVersion)
|
|
if maxErr != nil {
|
|
return maxErr
|
|
}
|
|
version = uint(maxValue) + 1
|
|
}
|
|
data := g.Map{c.TenantId: tenantID, c.PolicyVersion: version, c.PolicyName: in.PolicyName, c.PublishStatus: consts.ImSecurityPublishDraft, c.PasswordPolicy: policyJSON(in.PasswordPolicy, "{}"), c.LoginRateLimit: policyJSON(in.LoginRateLimit, "{}"), c.SensitiveWordPolicy: normalizedModerationPolicy(in.SensitiveWordPolicy, tenantCode), c.FileReviewPolicy: policyJSON(in.FileReviewPolicy, "{}"), c.RealnamePolicy: policyJSON(in.RealnamePolicy, "{}"), c.RiskChallengePolicy: policyJSON(in.RiskChallengePolicy, "{}"), c.GlobalFriendEnabled: in.GlobalFriendEnabled, c.FriendDisabledPolicy: policyJSON(in.FriendDisabledPolicy, `{"hide_profile":true,"block_qrcode":true,"block_group_member_entry":true,"blocked_entries":["search","profile_card","group_member","qrcode","invite_auto_add","recommendation"],"message":"当前时段暂不允许添加好友"}`), c.MessageCurfewEnabled: in.MessageCurfewEnabled, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: operator}
|
|
if in.Id > 0 && in.LoginRateLimit == nil {
|
|
// 未提交登录失败策略时保留原值(登录失败锁定已迁移至独立配置入口,避免保存草稿清空配置)
|
|
delete(data, c.LoginRateLimit)
|
|
}
|
|
if in.Id > 0 {
|
|
if _, updateErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(data).Update(); updateErr != nil {
|
|
return updateErr
|
|
}
|
|
} else {
|
|
data[c.CreatedAt] = now
|
|
data[c.CreatedBy] = operator
|
|
result, insertErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Data(data).Insert()
|
|
if insertErr != nil {
|
|
return insertErr
|
|
}
|
|
id, insertErr = result.LastInsertId()
|
|
if insertErr != nil {
|
|
return insertErr
|
|
}
|
|
}
|
|
if _, deleteErr := tx.Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Where(rc.PolicyId, id).Delete(); deleteErr != nil {
|
|
return deleteErr
|
|
}
|
|
for _, rule := range in.CurfewRules {
|
|
_, insertErr := tx.Model(dao.ImMessageCurfewRules.Table()).Safe().Ctx(ctx).Data(g.Map{rc.TenantId: tenantID, rc.PolicyId: id, rc.RuleCode: rule.RuleCode, rc.RuleName: rule.RuleName, rc.DateType: rule.DateType, rc.CustomDates: policyJSON(rule.CustomDates, "[]"), rc.Weekdays: policyJSON(rule.Weekdays, "[]"), rc.Timezone: rule.Timezone, rc.StartTime: rule.StartTime, rc.EndTime: rule.EndTime, rc.Scope: rule.Scope, rc.WhitelistUserIds: policyJSON(rule.WhitelistUserIds, "[]"), rc.WhitelistGroupIds: policyJSON(rule.WhitelistGroupIds, "[]"), rc.Message: rule.Message, rc.Status: rule.Status, rc.Sort: rule.Sort, rc.CreatedAt: now, rc.CreatedBy: operator, rc.UpdatedAt: now, rc.UpdatedBy: operator}).Insert()
|
|
if insertErr != nil {
|
|
return insertErr
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return 0, gerror.Wrap(err, "保存安全策略草稿失败")
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_policy_save", "im_security_policy", id, "success", g.Map{"global_friend_enabled": in.GlobalFriendEnabled, "message_curfew_enabled": in.MessageCurfewEnabled})
|
|
return id, nil
|
|
}
|
|
|
|
func normalizedModerationPolicy(value *gjson.Json, _ string) string {
|
|
policy := map[string]any{
|
|
"scenes": map[string]bool{"user_nickname": true, "user_signature": true, "group_name": true, "group_nickname": true, "private_message": true, "group_message": true, "group_announcement": true},
|
|
"risk_actions": map[string]string{"high": "block", "medium": "block", "low": "audit_only", "none": "allow"},
|
|
"failure_mode": "allow", "block_message": "内容不符合平台规范,请修改后重试",
|
|
}
|
|
if value != nil && value.String() != "" && value.String() != "null" {
|
|
for key, item := range value.Map() {
|
|
policy[key] = item
|
|
}
|
|
}
|
|
return gjson.New(policy).String()
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) Publish(ctx context.Context, in *sysin.ImSecurityPolicyPublishInp) error {
|
|
if err := in.Filter(ctx); err != nil {
|
|
return err
|
|
}
|
|
db := imtenant.RegistryDB()
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
var row gdb.Record
|
|
var err error
|
|
if row, err = db.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil {
|
|
return err
|
|
}
|
|
if row.IsEmpty() {
|
|
return gerror.New("安全策略不存在")
|
|
}
|
|
if row[c.PublishStatus].String() != consts.ImSecurityPublishDraft {
|
|
return gerror.New("只有草稿状态可以发布")
|
|
}
|
|
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, row[c.TenantId].Int64())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
operator := contexts.GetUserId(ctx)
|
|
now := gtime.Now()
|
|
err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
if _, updateErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished).WhereNot(c.Id, in.Id).WhereNull(c.DeletedAt).Data(g.Map{c.PublishStatus: consts.ImSecurityPublishArchived, c.UpdatedAt: now, c.UpdatedBy: operator}).Update(); updateErr != nil {
|
|
return updateErr
|
|
}
|
|
if _, updateErr := tx.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(g.Map{c.PublishStatus: consts.ImSecurityPublishPublished, c.PublishedAt: now, c.PublishedBy: operator, c.UpdatedAt: now, c.UpdatedBy: operator}).Update(); updateErr != nil {
|
|
return updateErr
|
|
}
|
|
if tenantID > 0 {
|
|
tc := dao.ImTenant.Columns()
|
|
if _, updateErr := tx.Model(dao.ImTenant.Table()).Safe().Ctx(ctx).Where(tc.Id, tenantID).Data(g.Map{tc.SecurityPolicyVersion: row[c.PolicyVersion].Uint(), tc.GlobalFriendEnabled: row[c.GlobalFriendEnabled].Int(), tc.MessageCurfewEnabled: row[c.MessageCurfewEnabled].Int(), tc.UpdatedAt: now, tc.UpdatedBy: operator}).Update(); updateErr != nil {
|
|
return updateErr
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return gerror.Wrap(err, "发布安全策略失败")
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_policy_publish", "im_security_policy", in.Id, "success", g.Map{"reason": in.Reason, "version": row[c.PolicyVersion].Uint()})
|
|
return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", row[c.PolicyVersion].Uint())
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) RestoreDefault(ctx context.Context, in *sysin.ImSecurityPolicyRestoreInp) (int64, error) {
|
|
if err := in.Filter(ctx); err != nil {
|
|
return 0, err
|
|
}
|
|
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
var source *sysin.ImSecurityPolicyModel
|
|
err = imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).
|
|
Fields(securityPolicyFields("")...).Where(c.TenantId, 0).
|
|
Where(c.PublishStatus, consts.ImSecurityPublishPublished).WhereNull(c.DeletedAt).
|
|
OrderDesc(c.PolicyVersion).Scan(&source)
|
|
if err != nil {
|
|
return 0, gerror.Wrap(err, "读取全局默认安全策略失败")
|
|
}
|
|
if source == nil {
|
|
return 0, gerror.New("全局默认安全策略不存在")
|
|
}
|
|
source.CurfewRules, err = s.curfewRules(ctx, source.Id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
rules := make([]*sysin.ImMessageCurfewRuleInp, 0, len(source.CurfewRules))
|
|
for _, rule := range source.CurfewRules {
|
|
rules = append(rules, &sysin.ImMessageCurfewRuleInp{RuleCode: rule.RuleCode, RuleName: rule.RuleName, DateType: rule.DateType, CustomDates: gjson.New(rule.CustomDates), Weekdays: gjson.New(rule.Weekdays), Timezone: rule.Timezone, StartTime: rule.StartTime, EndTime: rule.EndTime, Scope: rule.Scope, WhitelistUserIds: gjson.New(rule.WhitelistUserIds), WhitelistGroupIds: gjson.New(rule.WhitelistGroupIds), Message: rule.Message, Status: rule.Status, Sort: rule.Sort})
|
|
}
|
|
id, err := s.Save(ctx, &sysin.ImSecurityPolicySaveInp{TenantId: tenantID, PolicyName: "恢复默认安全策略", PasswordPolicy: gjson.New(source.PasswordPolicy), LoginRateLimit: gjson.New(source.LoginRateLimit), SensitiveWordPolicy: gjson.New(source.SensitiveWordPolicy), FileReviewPolicy: gjson.New(source.FileReviewPolicy), RealnamePolicy: gjson.New(source.RealnamePolicy), RiskChallengePolicy: gjson.New(source.RiskChallengePolicy), GlobalFriendEnabled: source.GlobalFriendEnabled, FriendDisabledPolicy: gjson.New(source.FriendDisabledPolicy), MessageCurfewEnabled: source.MessageCurfewEnabled, Remark: in.Reason, CurfewRules: rules})
|
|
if err == nil {
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_policy_restore_default", "im_security_policy", id, "success", g.Map{"reason": in.Reason})
|
|
}
|
|
return id, err
|
|
}
|
|
|
|
// GetLoginLockoutConfig 读取租户已发布策略中的登录失败锁定配置,无配置时返回默认值。
|
|
func (s *sSysImSecurityPolicy) GetLoginLockoutConfig(ctx context.Context, in *sysin.ImLoginLockoutConfigGetInp) (res *sysin.ImLoginLockoutConfigModel, err error) {
|
|
if err = in.Filter(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res = &sysin.ImLoginLockoutConfigModel{Enabled: false, PasswordMaxAttempts: 5, LockMinutes: 1440, WindowMinutes: 60}
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
var row gdb.Record
|
|
if row, err = imtenant.RegistryDB().Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).
|
|
Fields(c.Id, c.PolicyVersion, "CAST("+c.LoginRateLimit+" AS CHAR) login_rate_limit").
|
|
Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished).
|
|
WhereNull(c.DeletedAt).OrderDesc(c.PolicyVersion).One(); err != nil {
|
|
return nil, gerror.Wrap(err, "获取登录失败策略配置失败")
|
|
}
|
|
if row.IsEmpty() {
|
|
return res, nil
|
|
}
|
|
res.PolicyId = row[c.Id].Int64()
|
|
res.PolicyVersion = row[c.PolicyVersion].Uint()
|
|
policy := gjson.New(row["login_rate_limit"].String())
|
|
res.Enabled = policy.Get("enabled").Bool()
|
|
if v := policy.Get("password_max_attempts").Int(); v >= 1 {
|
|
res.PasswordMaxAttempts = int(v)
|
|
}
|
|
if v := policy.Get("lock_seconds").Int(); v >= 60 {
|
|
res.LockMinutes = int(v / 60)
|
|
}
|
|
if v := policy.Get("window_seconds").Int(); v >= 60 {
|
|
res.WindowMinutes = int(v / 60)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
// SaveLoginLockoutConfig 保存租户登录失败锁定配置:直接更新已发布策略,保存后通过策略变更事件即时生效。
|
|
func (s *sSysImSecurityPolicy) SaveLoginLockoutConfig(ctx context.Context, in *sysin.ImLoginLockoutConfigSaveInp) error {
|
|
if err := in.Filter(ctx); err != nil {
|
|
return err
|
|
}
|
|
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value := map[string]any{"enabled": false}
|
|
if in.Enabled {
|
|
value = map[string]any{"enabled": true, "password_max_attempts": in.PasswordMaxAttempts, "lock_seconds": in.LockMinutes * 60, "window_seconds": in.WindowMinutes * 60}
|
|
}
|
|
loginRateLimit := gjson.New(value).String()
|
|
c := dao.ImSecurityPolicy.Columns()
|
|
db := imtenant.RegistryDB()
|
|
var row gdb.Record
|
|
if row, err = db.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).
|
|
Fields(c.Id, c.PolicyVersion).
|
|
Where(c.TenantId, tenantID).Where(c.PublishStatus, consts.ImSecurityPublishPublished).
|
|
WhereNull(c.DeletedAt).OrderDesc(c.PolicyVersion).One(); err != nil {
|
|
return gerror.Wrap(err, "获取登录失败策略配置失败")
|
|
}
|
|
operator := contexts.GetUserId(ctx)
|
|
now := gtime.Now()
|
|
if !row.IsEmpty() {
|
|
policyID := row[c.Id].Int64()
|
|
version := row[c.PolicyVersion].Uint()
|
|
if _, err = db.Model(dao.ImSecurityPolicy.Table()).Safe().Ctx(ctx).Where(c.Id, policyID).
|
|
Data(g.Map{c.LoginRateLimit: loginRateLimit, c.UpdatedAt: now, c.UpdatedBy: operator}).Update(); err != nil {
|
|
return gerror.Wrap(err, "保存登录失败策略配置失败")
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_policy_login_lockout_save", "im_security_policy", policyID, "success", g.Map{"enabled": in.Enabled, "password_max_attempts": in.PasswordMaxAttempts, "lock_minutes": in.LockMinutes, "window_minutes": in.WindowMinutes})
|
|
return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", version)
|
|
}
|
|
// 租户尚无已发布策略:创建默认策略草稿并直接发布(Publish 内部已发送策略变更事件)
|
|
id, saveErr := s.Save(ctx, &sysin.ImSecurityPolicySaveInp{TenantId: tenantID, PolicyName: "默认安全策略", LoginRateLimit: gjson.New(value), GlobalFriendEnabled: 1, MessageCurfewEnabled: 0})
|
|
if saveErr != nil {
|
|
return saveErr
|
|
}
|
|
if err = s.Publish(ctx, &sysin.ImSecurityPolicyPublishInp{Id: id, Reason: "登录失败策略首次配置"}); err != nil {
|
|
return err
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_policy_login_lockout_save", "im_security_policy", id, "success", g.Map{"enabled": in.Enabled, "password_max_attempts": in.PasswordMaxAttempts, "lock_minutes": in.LockMinutes, "window_minutes": in.WindowMinutes})
|
|
return nil
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) IPRuleList(ctx context.Context, in *sysin.ImSecurityIPRuleListInp) (list []*sysin.ImSecurityIPRuleModel, total int, err error) {
|
|
if err = in.Filter(ctx); err != nil {
|
|
return
|
|
}
|
|
tenantID, _, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
c := dao.ImSecurityIpRules.Columns()
|
|
model := imtenant.RegistryDB().Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Fields(c.Id, c.TenantId, c.RuleType, c.IpCidr, c.Action, "CAST("+c.SceneJson+" AS CHAR) scene_json", c.RiskLevel, c.Status, c.ExpiresAt, c.Remark, c.CreatedAt, c.UpdatedAt).Where(c.TenantId, tenantID).WhereNull(c.DeletedAt)
|
|
if in.RuleType != "" {
|
|
model = model.Where(c.RuleType, in.RuleType)
|
|
}
|
|
if in.IpCidr != "" {
|
|
model = model.WhereLike(c.IpCidr, "%"+in.IpCidr+"%")
|
|
}
|
|
if in.Status != 0 {
|
|
model = model.Where(c.Status, in.Status)
|
|
}
|
|
err = model.Page(in.Page, in.PerPage).OrderDesc(c.Id).ScanAndCount(&list, &total, false)
|
|
return
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) IPRuleEdit(ctx context.Context, in *sysin.ImSecurityIPRuleEditInp) error {
|
|
if err := in.Filter(ctx); err != nil {
|
|
return err
|
|
}
|
|
if net.ParseIP(in.IpCidr) == nil {
|
|
if _, _, err := net.ParseCIDR(in.IpCidr); err != nil {
|
|
return gerror.New("IP或CIDR格式不合法")
|
|
}
|
|
}
|
|
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, in.TenantId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c := dao.ImSecurityIpRules.Columns()
|
|
db := imtenant.RegistryDB()
|
|
now := gtime.Now()
|
|
data := g.Map{c.TenantId: tenantID, c.RuleType: in.RuleType, c.IpCidr: in.IpCidr, c.Action: in.Action, c.SceneJson: policyJSON(in.SceneJson, "[]"), c.RiskLevel: in.RiskLevel, c.Status: in.Status, c.ExpiresAt: in.ExpiresAt, c.Remark: in.Remark, c.UpdatedAt: now, c.UpdatedBy: contexts.GetUserId(ctx)}
|
|
targetID := in.Id
|
|
if in.Id > 0 {
|
|
var row gdb.Record
|
|
if row, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil {
|
|
return err
|
|
}
|
|
if row.IsEmpty() || row[c.TenantId].Int64() != tenantID {
|
|
return gerror.New("IP规则不存在")
|
|
}
|
|
_, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(data).Update()
|
|
} else {
|
|
data[c.CreatedAt] = now
|
|
data[c.CreatedBy] = contexts.GetUserId(ctx)
|
|
result, insertErr := db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Data(data).Insert()
|
|
err = insertErr
|
|
if err == nil {
|
|
targetID, err = result.LastInsertId()
|
|
}
|
|
}
|
|
if err != nil {
|
|
return gerror.Wrap(err, "保存IP规则失败")
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_ip_rule_edit", "im_security_ip_rule", targetID, "success", g.Map{"ip_cidr": in.IpCidr, "rule_type": in.RuleType})
|
|
return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", uint(targetID))
|
|
}
|
|
|
|
func (s *sSysImSecurityPolicy) IPRuleDelete(ctx context.Context, in *sysin.ImSecurityIPRuleDeleteInp) error {
|
|
if err := in.Filter(ctx); err != nil {
|
|
return err
|
|
}
|
|
c := dao.ImSecurityIpRules.Columns()
|
|
db := imtenant.RegistryDB()
|
|
var row gdb.Record
|
|
var err error
|
|
if row, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).WhereNull(c.DeletedAt).One(); err != nil {
|
|
return err
|
|
}
|
|
if row.IsEmpty() {
|
|
return gerror.New("IP规则不存在")
|
|
}
|
|
tenantID, tenantCode, err := resolveImPolicyTenant(ctx, row[c.TenantId].Int64())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = db.Model(dao.ImSecurityIpRules.Table()).Safe().Ctx(ctx).Where(c.Id, in.Id).Data(g.Map{c.Status: consts.StatusDisable, c.DeletedAt: gtime.Now(), c.DeletedBy: contexts.GetUserId(ctx), c.UpdatedAt: gtime.Now(), c.UpdatedBy: contexts.GetUserId(ctx)}).Update()
|
|
if err != nil {
|
|
return gerror.Wrap(err, "删除IP规则失败")
|
|
}
|
|
_ = writeImAdminAudit(ctx, tenantID, "im_security_ip_rule_delete", "im_security_ip_rule", in.Id, "success", nil)
|
|
return publishImPolicyEvent(ctx, tenantID, tenantCode, "security.policy.updated", "security_policy", uint(in.Id))
|
|
}
|