feat: publish password reset via kafka
This commit is contained in:
@@ -38,7 +38,9 @@ type ResetPasswordReq struct {
|
|||||||
sysin.ImUserPasswordResetInp
|
sysin.ImUserPasswordResetInp
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordRes struct{}
|
type ResetPasswordRes struct {
|
||||||
|
*sysin.ImUserCommandModel
|
||||||
|
}
|
||||||
|
|
||||||
type RevokeSessionsReq struct {
|
type RevokeSessionsReq struct {
|
||||||
g.Meta `path:"/imUser/revokeSessions" method:"post" tags:"IM用户" summary:"撤销IM用户会话"`
|
g.Meta `path:"/imUser/revokeSessions" method:"post" tags:"IM用户" summary:"撤销IM用户会话"`
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const (
|
|||||||
ImAdminCommandUserLogoutAll = "user.logout_all"
|
ImAdminCommandUserLogoutAll = "user.logout_all"
|
||||||
ImAdminCommandUserCancel = "user.account.cancel"
|
ImAdminCommandUserCancel = "user.account.cancel"
|
||||||
ImAdminCommandUserDelete = "user.account.delete"
|
ImAdminCommandUserDelete = "user.account.delete"
|
||||||
|
ImAdminCommandUserPasswordReset = "user.password.reset"
|
||||||
ImAdminCommandKafkaDeadReplay = "kafka.dead_letter.replay"
|
ImAdminCommandKafkaDeadReplay = "kafka.dead_letter.replay"
|
||||||
ImAdminCommandKafkaDeadIgnore = "kafka.dead_letter.ignore"
|
ImAdminCommandKafkaDeadIgnore = "kafka.dead_letter.ignore"
|
||||||
ImAdminCommandKafkaDeadResolve = "kafka.dead_letter.resolve"
|
ImAdminCommandKafkaDeadResolve = "kafka.dead_letter.resolve"
|
||||||
|
|||||||
@@ -48,7 +48,12 @@ func (c *cImUser) View(ctx context.Context, req *imuser.ViewReq) (res *imuser.Vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *cImUser) ResetPassword(ctx context.Context, req *imuser.ResetPasswordReq) (res *imuser.ResetPasswordRes, err error) {
|
func (c *cImUser) ResetPassword(ctx context.Context, req *imuser.ResetPasswordReq) (res *imuser.ResetPasswordRes, err error) {
|
||||||
err = service.SysImUser().ResetPassword(ctx, &req.ImUserPasswordResetInp)
|
data, err := service.SysImUser().ResetPassword(ctx, &req.ImUserPasswordResetInp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = new(imuser.ResetPasswordRes)
|
||||||
|
res.ImUserCommandModel = data
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package sys
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"hotgo/internal/library/contexts"
|
"hotgo/internal/library/contexts"
|
||||||
"hotgo/internal/library/imkafka"
|
"hotgo/internal/library/imkafka"
|
||||||
"hotgo/internal/library/imtenant"
|
"hotgo/internal/library/imtenant"
|
||||||
|
"hotgo/internal/model/entity"
|
||||||
"hotgo/internal/model/input/sysin"
|
"hotgo/internal/model/input/sysin"
|
||||||
"hotgo/internal/service"
|
"hotgo/internal/service"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -209,29 +211,25 @@ func (s *sSysImUser) viewFields() []interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sSysImUser) ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (err error) {
|
func (s *sSysImUser) ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (res *sysin.ImUserCommandModel, err error) {
|
||||||
if err = in.Filter(ctx); err != nil {
|
if err = in.Filter(ctx); err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
scope, err := resolveImAdminScope(ctx, in.TenantId)
|
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
userId, err := sysin.ParseOptionalInt64(in.UserId)
|
userId, err := sysin.ParseOptionalInt64(in.UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
db, _, err := imtenant.DB(ctx, scope.TenantId)
|
command, err := s.publishPasswordResetCommand(ctx, tenant, userId, in.NewPassword)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = resetIMUserPassword(ctx, db, scope, userId, in.NewPassword)
|
|
||||||
result := "success"
|
result := "success"
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result = "failed"
|
result = "failed"
|
||||||
}
|
}
|
||||||
_ = writeImAdminAudit(ctx, scope.TenantId, "user_password_reset", "user", userId, result, nil)
|
_ = writeImAdminAudit(ctx, tenant.Id, "user_password_reset", "user", userId, result, nil)
|
||||||
return err
|
return command, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sSysImUser) RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error) {
|
func (s *sSysImUser) RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error) {
|
||||||
@@ -391,6 +389,50 @@ func (s *sSysImUser) publishGovernanceCommand(ctx context.Context, in *sysin.ImU
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sSysImUser) publishPasswordResetCommand(ctx context.Context, tenant *entity.ImTenant, userId int64, newPassword string) (*sysin.ImUserCommandModel, error) {
|
||||||
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gerror.Wrap(err, "生成密码摘要失败")
|
||||||
|
}
|
||||||
|
user := contexts.GetUser(ctx)
|
||||||
|
actorName := ""
|
||||||
|
if user != nil {
|
||||||
|
actorName = user.RealName
|
||||||
|
}
|
||||||
|
payload := map[string]any{
|
||||||
|
"operator_id": strconv.FormatInt(contexts.GetUserId(ctx), 10),
|
||||||
|
"user_id": strconv.FormatInt(userId, 10),
|
||||||
|
"target_user_id": strconv.FormatInt(userId, 10),
|
||||||
|
"password_hash": base64.StdEncoding.EncodeToString(passwordHash),
|
||||||
|
}
|
||||||
|
command, err := imkafka.PublishAdminCommand(ctx, imkafka.AdminCommandInput{
|
||||||
|
TenantID: tenant.Id,
|
||||||
|
TenantCode: tenant.Code,
|
||||||
|
EventType: consts.ImAdminCommandUserPasswordReset,
|
||||||
|
BizType: "user",
|
||||||
|
BizID: strconv.FormatInt(userId, 10),
|
||||||
|
Actor: &imkafka.Actor{
|
||||||
|
Type: "admin",
|
||||||
|
ID: strconv.FormatInt(contexts.GetUserId(ctx), 10),
|
||||||
|
DisplayName: actorName,
|
||||||
|
},
|
||||||
|
Payload: payload,
|
||||||
|
CreatedBy: contexts.GetUserId(ctx),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &sysin.ImUserCommandModel{
|
||||||
|
CommandID: command.CommandID,
|
||||||
|
EventID: command.EventID,
|
||||||
|
Status: command.Status,
|
||||||
|
TenantID: command.TenantID,
|
||||||
|
TenantCode: command.TenantCode,
|
||||||
|
EventType: command.EventType,
|
||||||
|
IdempotencyKey: command.IdempotencyKey,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func updateIMUserStatus(ctx context.Context, db gdb.DB, scope imAdminScope, userId int64, state int) error {
|
func updateIMUserStatus(ctx context.Context, db gdb.DB, scope imAdminScope, userId int64, state int) error {
|
||||||
securityId, err := imtenant.NextSnowflakeID(ctx)
|
securityId, err := imtenant.NextSnowflakeID(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ type (
|
|||||||
// View 获取IM用户指定信息
|
// View 获取IM用户指定信息
|
||||||
View(ctx context.Context, in *sysin.ImUserViewInp) (res *sysin.ImUserViewModel, err error)
|
View(ctx context.Context, in *sysin.ImUserViewInp) (res *sysin.ImUserViewModel, err error)
|
||||||
// ResetPassword 管理员重置IM用户密码
|
// ResetPassword 管理员重置IM用户密码
|
||||||
ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (err error)
|
ResetPassword(ctx context.Context, in *sysin.ImUserPasswordResetInp) (res *sysin.ImUserCommandModel, err error)
|
||||||
// RevokeSessions 撤销IM用户会话
|
// RevokeSessions 撤销IM用户会话
|
||||||
RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error)
|
RevokeSessions(ctx context.Context, in *sysin.ImUserRevokeSessionsInp) (err error)
|
||||||
// Status 更新IM用户状态
|
// Status 更新IM用户状态
|
||||||
|
|||||||
Reference in New Issue
Block a user