Merge pull request #9891 from rainzm/notify/alert_fix

Repair of abnormal login notification
This commit is contained in:
Zexi Li
2021-01-15 10:31:01 +08:00
committed by GitHub
8 changed files with 108 additions and 34 deletions
@@ -1,5 +1,5 @@
{{- if .admin -}}
{{ .domain }}下的账号{{ .user }}由于异常登录已被锁定,请您核实用户使用情况。如果需要为用户解锁,请到用户列表启用该用户。
{{ .domain }}下的账号{{ .user }}由于异常登录已被锁定,请您核实用户使用情况。如果需要为用户解锁,请到用户列表启用该用户。
{{- else -}}
您的账号{{ .user }}由于异常登录已被锁定,请联系管理员解锁账号。
{{- end -}}
@@ -1 +1 @@
安全告警
异常登录
@@ -1 +1 @@
Security Alerts
Abnormal Login
+40 -1
View File
@@ -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 {
+22 -18
View File
@@ -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)
}
+13
View File
@@ -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 {
+26 -9
View File
@@ -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
+4 -3
View File
@@ -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 {