Files
im_manager/server/internal/logic/sys/im_security_policy.go
T
Austin 6348ace38e feat(im security): add login failure lockout configuration feature
1. add APIs for login lockout config get and save
2. add backend service and controller for login lockout config
3. refactor frontend to separate login lockout config from main policy
4. update default login lockout policy values
5. add permission sql and docking documentation
2026-09-21 09:28:03 +08:00

434 lines
22 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.ImTenantConfig.Columns()
if _, updateErr := tx.Model(dao.ImTenantConfig.Table()).Safe().Ctx(ctx).Where(tc.TenantId, 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))
}