mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-21 14:19:49 +08:00
Merge pull request #9891 from rainzm/notify/alert_fix
Repair of abnormal login notification
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{{- if .admin -}}
|
||||
域{{ .domain }}下的账号{{ .user }}由于异常登录已被锁定,请您核实用户使用情况。如果需要为用户解锁,请到用户列表启用该用户。
|
||||
{{ .domain }}域下的账号{{ .user }}由于异常登录已被锁定,请您核实用户使用情况。如果需要为用户解锁,请到用户列表启用该用户。
|
||||
{{- else -}}
|
||||
您的账号{{ .user }}由于异常登录已被锁定,请联系管理员解锁账号。
|
||||
{{- end -}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
安全告警
|
||||
异常登录
|
||||
|
||||
@@ -1 +1 @@
|
||||
Security Alerts
|
||||
Abnormal Login
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -56,6 +57,8 @@ var (
|
||||
notifyclientI18nTable = i18n.Table{}
|
||||
AdminSessionGenerator SAdminSessionGenerator = getAdminSesion
|
||||
UserLangFetcher SUserLangFetcher = getUserLang
|
||||
topicWithTemplateSet = &sync.Map{}
|
||||
checkTemplates bool
|
||||
)
|
||||
|
||||
type SAdminSessionGenerator func(ctx context.Context, region string, apiVersion string) (*mcclient.ClientSession, error)
|
||||
@@ -102,6 +105,29 @@ func init() {
|
||||
notifyclientI18nTable.Set(SUFFIX, i18n.NewTableEntry().EN("en").CN("cn"))
|
||||
}
|
||||
|
||||
func hasTemplateOfTopic(topic string) bool {
|
||||
if checkTemplates {
|
||||
_, ok := topicWithTemplateSet.Load(topic)
|
||||
return ok
|
||||
}
|
||||
path := filepath.Join(consts.NotifyTemplateDir, consts.GetServiceType(), "content@cn")
|
||||
fileInfoList, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
checkTemplates = true
|
||||
return false
|
||||
}
|
||||
log.Errorf("unable to read dir %s", path)
|
||||
return false
|
||||
}
|
||||
for i := range fileInfoList {
|
||||
topicWithTemplateSet.Store(fileInfoList[i].Name(), nil)
|
||||
}
|
||||
checkTemplates = true
|
||||
_, ok := topicWithTemplateSet.Load(topic)
|
||||
return ok
|
||||
}
|
||||
|
||||
func getTemplateString(suffix string, topic string, contType string, channel npk.TNotifyChannel) ([]byte, error) {
|
||||
contType = contType + "@" + suffix
|
||||
if len(channel) > 0 {
|
||||
@@ -364,6 +390,20 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
|
||||
reIds = p.recipientId
|
||||
}
|
||||
|
||||
if !hasTemplateOfTopic(p.event) {
|
||||
msg := npk.SNotifyMessage{}
|
||||
msg.Uid = reIds
|
||||
msg.Priority = p.priority
|
||||
msg.Contacts = p.contacts
|
||||
msg.ContactType = p.channel
|
||||
msg.Topic = p.event
|
||||
msg.Msg = p.data.String()
|
||||
msg.Tag = p.tag
|
||||
msg.Metadata = p.metadata
|
||||
msg.IgnoreNonexistentReceiver = p.ignoreNonexistentReceiver
|
||||
return []npk.SNotifyMessage{msg}, nil
|
||||
}
|
||||
|
||||
langMap, err := lang(ctx, p.channel, reIds, p.contacts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -403,7 +443,6 @@ func intelliNotify(ctx context.Context, p sNotifyParams) {
|
||||
}
|
||||
for i := range msgs {
|
||||
msg := msgs[i]
|
||||
log.Infof("msg: %s", jsonutils.Marshal(msg))
|
||||
notifyClientWorkerMan.Run(func() {
|
||||
s, err := AdminSessionGenerator(context.Background(), consts.GetRegion(), "")
|
||||
if err != nil {
|
||||
|
||||
@@ -82,7 +82,25 @@ func (sql *SSQLDriver) Authenticate(ctx context.Context, ident mcclient.SAuthent
|
||||
}
|
||||
|
||||
func (sql *SSQLDriver) alertNotify(ctx context.Context, uext *api.SUserExtended, triggerTime time.Time) {
|
||||
// get all users
|
||||
// users
|
||||
data := jsonutils.NewDict()
|
||||
data.Set("user", jsonutils.NewString(uext.Name))
|
||||
data.Set("domain", jsonutils.NewString(uext.DomainName))
|
||||
metadata := map[string]interface{}{
|
||||
"trigger_time": triggerTime,
|
||||
}
|
||||
p := notifyclient.SNotifyParams{
|
||||
RecipientId: []string{uext.Id},
|
||||
Priority: notify.NotifyPriorityCritical,
|
||||
Event: notifyclient.USER_LOGIN_EXCEPTION,
|
||||
Data: data,
|
||||
Tag: noapi.NOTIFICATION_TAG_ALERT,
|
||||
Metadata: metadata,
|
||||
IgnoreNonexistentReceiver: true,
|
||||
}
|
||||
notifyclient.NotifyWithTag(ctx, p)
|
||||
|
||||
// admin user
|
||||
daUserIds, err := getDomainAdminUserIds(uext.DomainName)
|
||||
if err != nil {
|
||||
log.Errorf("unable to get user with role domainadmin in domain %s: %v", uext.DomainName, err)
|
||||
@@ -94,23 +112,9 @@ func (sql *SSQLDriver) alertNotify(ctx context.Context, uext *api.SUserExtended,
|
||||
userSet := sets.NewString(daUserIds...)
|
||||
userSet.Insert(aUserIds...)
|
||||
userSet.Insert(uext.Id)
|
||||
|
||||
data := jsonutils.NewDict()
|
||||
data.Set("user", jsonutils.NewString(uext.Name))
|
||||
data.Set("domain", jsonutils.NewString(uext.DomainName))
|
||||
metadata := map[string]interface{}{
|
||||
"trigger_time": triggerTime,
|
||||
}
|
||||
// user
|
||||
p := notifyclient.SNotifyParams{
|
||||
RecipientId: userSet.UnsortedList(),
|
||||
Priority: notify.NotifyPriorityCritical,
|
||||
Event: notifyclient.USER_LOGIN_EXCEPTION,
|
||||
Data: data,
|
||||
Tag: noapi.NOTIFICATION_TAG_ALERT,
|
||||
Metadata: metadata,
|
||||
IgnoreNonexistentReceiver: true,
|
||||
}
|
||||
data.Set("admin", jsonutils.JSONTrue)
|
||||
p.RecipientId = userSet.UnsortedList()
|
||||
p.Data = data
|
||||
notifyclient.NotifyWithTag(ctx, p)
|
||||
}
|
||||
|
||||
|
||||
@@ -735,6 +735,19 @@ func (user *SUser) PostUpdate(ctx context.Context, userCred mcclient.TokenCreden
|
||||
}
|
||||
logclient.AddActionLogWithContext(ctx, user, logclient.ACT_UPDATE_PASSWORD, nil, userCred, true)
|
||||
}
|
||||
if enabled, _ := data.Bool("enabled"); enabled {
|
||||
localUser, err := LocalUserManager.fetchLocalUser(user.Id, user.DomainId, 0)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return
|
||||
}
|
||||
log.Errorf("unable to fetch localUser of user %q in domain %q: %v", user.Id, user.DomainId, err)
|
||||
return
|
||||
}
|
||||
if err = localUser.ClearFailedAuth(); err != nil {
|
||||
log.Errorf("unable to clear failed auth: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (user *SUser) ValidateDeleteCondition(ctx context.Context) error {
|
||||
|
||||
@@ -201,7 +201,7 @@ func (nm *SNotificationManager) FetchCustomizeColumns(
|
||||
|
||||
var err error
|
||||
for i := range rows {
|
||||
rows[i], err = objs[i].(*SNotification).getMoreDetails(ctx, query, rows[i])
|
||||
rows[i], err = objs[i].(*SNotification).getMoreDetails(ctx, userCred, query, rows[i])
|
||||
if err != nil {
|
||||
log.Errorf("Notification.getMoreDetails: %v", err)
|
||||
}
|
||||
@@ -233,11 +233,25 @@ func (n *SNotification) ReceiverNotificationsNotOK() ([]SReceiverNotification, e
|
||||
return rns, nil
|
||||
}
|
||||
|
||||
func (n *SNotification) ReceiveDetails() ([]api.ReceiveDetail, error) {
|
||||
subRQ := ReceiverManager.Query("id", "name").SubQuery()
|
||||
func (n *SNotification) ReceiveDetails(userCred mcclient.TokenCredential, scope string) ([]api.ReceiveDetail, error) {
|
||||
RQ := ReceiverManager.Query("id", "name")
|
||||
q := ReceiverNotificationManager.Query("receiver_id", "notification_id", "contact", "send_at", "send_by", "status", "failed_reason").Equals("notification_id", n.Id)
|
||||
q.AppendField(subRQ.Field("name", "receiver_name"))
|
||||
q = q.LeftJoin(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
|
||||
s := rbacutils.TRbacScope(scope)
|
||||
|
||||
switch s {
|
||||
case rbacutils.ScopeSystem:
|
||||
subRQ := RQ.SubQuery()
|
||||
q.AppendField(subRQ.Field("name", "receiver_name"))
|
||||
q = q.LeftJoin(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
|
||||
case rbacutils.ScopeDomain:
|
||||
subRQ := RQ.Equals("domain_id", userCred.GetDomainId()).SubQuery()
|
||||
q.AppendField(subRQ.Field("name", "receiver_name"))
|
||||
q = q.Join(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
|
||||
default:
|
||||
subRQ := RQ.Equals("id", userCred.GetUserId()).SubQuery()
|
||||
q.AppendField(subRQ.Field("name", "receiver_name"))
|
||||
q = q.Join(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
|
||||
}
|
||||
ret := make([]api.ReceiveDetail, 0, 2)
|
||||
err := q.All(&ret)
|
||||
if err != nil && errors.Cause(err) != sql.ErrNoRows {
|
||||
@@ -247,7 +261,7 @@ func (n *SNotification) ReceiveDetails() ([]api.ReceiveDetail, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (n *SNotification) getMoreDetails(ctx context.Context, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
|
||||
func (n *SNotification) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
|
||||
// get title adn content
|
||||
p, err := TemplateManager.NotifyFilter(n.ContactType, n.Topic, n.Message, getTemplateLangFromCtx(ctx))
|
||||
if err != nil {
|
||||
@@ -255,8 +269,10 @@ func (n *SNotification) getMoreDetails(ctx context.Context, query jsonutils.JSON
|
||||
}
|
||||
out.Title = p.Title
|
||||
out.Content = p.Message
|
||||
|
||||
scope, _ := query.GetString("scope")
|
||||
// get receive details
|
||||
out.ReceiveDetails, err = n.ReceiveDetails()
|
||||
out.ReceiveDetails, err = n.ReceiveDetails(userCred, scope)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
@@ -294,10 +310,11 @@ func (nm *SNotificationManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient
|
||||
case rbacutils.ScopeDomain:
|
||||
subRq := ReceiverManager.Query("id").Equals("domain_id", owner.GetDomainId()).SubQuery()
|
||||
RNq := ReceiverNotificationManager.Query("notification_id", "receiver_id")
|
||||
subRNq := RNq.Join(subRq, sqlchemy.Equals(RNq.Field("receiver_id"), subRq.Field("id"))).SubQuery()
|
||||
subRNq := RNq.Join(subRq, sqlchemy.OR(sqlchemy.Equals(RNq.Field("receiver_id"), subRq.Field("id")), sqlchemy.Equals(RNq.Field("contact"), subRq.Field("id")))).SubQuery()
|
||||
q = q.Join(subRNq, sqlchemy.Equals(q.Field("id"), subRNq.Field("notification_id")))
|
||||
case rbacutils.ScopeProject, rbacutils.ScopeUser:
|
||||
subq := ReceiverNotificationManager.Query("notification_id").Equals("receiver_id", owner.GetUserId()).SubQuery()
|
||||
sq := ReceiverNotificationManager.Query("notification_id")
|
||||
subq := sq.Filter(sqlchemy.OR(sqlchemy.Equals(sq.Field("receiver_id"), owner.GetUserId()), sqlchemy.Equals(sq.Field("contact"), owner.GetUserId()))).SubQuery()
|
||||
q = q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("notification_id")))
|
||||
}
|
||||
return q
|
||||
|
||||
@@ -146,8 +146,9 @@ func (tm *STemplateManager) GetCompanyInfo(ctx context.Context) (SCompanyInfo, e
|
||||
}
|
||||
|
||||
var (
|
||||
ForceInitType = []string{
|
||||
api.EMAIL,
|
||||
forceInitTopic = []string{
|
||||
"VERIFY",
|
||||
"USER_LOGIN_EXCEPTION",
|
||||
}
|
||||
notifyclientI18nTable = i18n.Table{}
|
||||
defaultLang = api.TEMPLATE_LANG_CN
|
||||
@@ -187,7 +188,7 @@ func (tm *STemplateManager) InitializeData() error {
|
||||
for _, template := range templates {
|
||||
q := tm.Query().Equals("contact_type", template.ContactType).Equals("topic", template.Topic).Equals("template_type", template.TemplateType).Equals("lang", template.Lang)
|
||||
count, _ := q.CountWithError()
|
||||
if count > 0 && !utils.IsInStringArray(template.ContactType, ForceInitType) {
|
||||
if count > 0 && !utils.IsInStringArray(template.Topic, forceInitTopic) {
|
||||
continue
|
||||
}
|
||||
if count == 0 {
|
||||
|
||||
Reference in New Issue
Block a user