feat: 接入消息加密与实名认证能力

This commit is contained in:
richjohnrambo
2026-09-21 09:53:39 +08:00
parent 841c0c77cd
commit e3b93113f1
33 changed files with 1747 additions and 99 deletions
+159 -11
View File
@@ -174,6 +174,9 @@ type AdminInternalConfigProvider interface {
ModerationPolicy(tenantCode string) (*types.ModerationPolicy, error)
SensitiveWords(tenantCode string) (*types.SensitiveWordSet, error)
SecurityPolicy(tenantCode string) (*types.SecurityPolicy, error)
RealNamePolicy(tenantID types.TenantID) (*types.RealNamePolicy, error)
MessageKey(tenantCode string, version int) (*types.MessageKey, error)
MessageKeyAck(tenantCode string, version int) error
}
var adminInternalConfigProvider AdminInternalConfigProvider
@@ -319,6 +322,61 @@ func (thirdPartyConfigMapper) Get(group string, keys ...string) (map[string]stri
return result, err
}
type RealNameConfigPersistenceInterface interface {
Policy(tenantID types.TenantID) (*types.RealNamePolicy, error)
}
type realNameConfigMapper struct{}
var RealNameConfig RealNameConfigPersistenceInterface = realNameConfigMapper{}
func (realNameConfigMapper) Policy(tenantID types.TenantID) (*types.RealNamePolicy, error) {
if !tenantID.IsValid() {
return nil, types.ErrMalformed
}
cacheKey := formatTenantIDForCache(tenantID)
if cached, ok := realNamePolicyCache.get(cacheKey); ok {
cp := *cached
return &cp, nil
}
if adminInternalConfigProvider != nil {
if result, err := adminInternalConfigProvider.RealNamePolicy(tenantID); err == nil && result != nil {
result.Normalize()
realNamePolicyCache.set(cacheKey, result)
cp := *result
return &cp, nil
} else if err != nil {
logs.Warn.Printf("admin internal realname policy fallback failed: tenant=%d err=%v", tenantID, err)
}
}
configAdp, ok := adp.(adapter.RealNamePolicyConfigAdapter)
if !ok {
policy := defaultRealNamePolicy(tenantID)
realNamePolicyCache.set(cacheKey, policy)
return policy, nil
}
result, err := configAdp.RealNamePolicy(tenantID)
if err != nil {
return nil, err
}
result.Normalize()
realNamePolicyCache.set(cacheKey, result)
cp := *result
return &cp, nil
}
func defaultRealNamePolicy(tenantID types.TenantID) *types.RealNamePolicy {
policy := &types.RealNamePolicy{
TenantID: tenantID,
ReviewPolicy: types.RealNameReviewPolicyNone,
ProviderMode: "id_card_2factor",
AllowSkip: true,
RemindAfter: "24h",
}
policy.Normalize()
return policy
}
type TenantEntryPersistenceInterface interface {
CreatePublicUpload(upload *types.PublicUpload) (*types.PublicUpload, error)
CreateTenantApplication(application *types.TenantApplication) (*types.TenantApplication, error)
@@ -1490,10 +1548,16 @@ func (messagesMapper) GetOne(tenantID types.TenantID, topic string, seq int64) (
if err != nil {
return nil, err
}
var msg *types.Message
if types.GetTopicCat(topic) == types.TopicCatGrp {
return tenantAdp.TenantGroupMessageGetOne(tenantID, topic, seq)
msg, err = tenantAdp.TenantGroupMessageGetOne(tenantID, topic, seq)
} else {
msg, err = tenantAdp.TenantMessageGetOne(tenantID, topic, seq)
}
return tenantAdp.TenantMessageGetOne(tenantID, topic, seq)
if err == nil {
err = decryptMessageContent(msg)
}
return msg, err
}
func (messagesMapper) Recall(tenantID types.TenantID, topic string, seq int64, actor types.Uid, expectedVersion int, recalledAt time.Time) (*types.Message, error) {
@@ -1503,6 +1567,9 @@ func (messagesMapper) Recall(tenantID types.TenantID, topic string, seq int64, a
}
if types.GetTopicCat(topic) == types.TopicCatGrp {
msg, err := tenantAdp.TenantGroupMessageRecall(tenantID, topic, seq, actor, expectedVersion, recalledAt)
if err == nil {
err = decryptMessageContent(msg)
}
if err == nil && groupMsgRuntime != nil {
if cacheErr := groupMsgRuntime.CacheHotMessage(tenantID, msg); cacheErr != nil {
logs.Warn.Printf("topic[%s]: failed to refresh recalled group message cache: %v", topic, cacheErr)
@@ -1510,7 +1577,11 @@ func (messagesMapper) Recall(tenantID types.TenantID, topic string, seq int64, a
}
return msg, err
}
return tenantAdp.TenantMessageRecall(tenantID, topic, seq, actor, expectedVersion, recalledAt)
msg, err := tenantAdp.TenantMessageRecall(tenantID, topic, seq, actor, expectedVersion, recalledAt)
if err == nil {
err = decryptMessageContent(msg)
}
return msg, err
}
func (messagesMapper) Edit(tenantID types.TenantID, topic string, seq int64, actor types.Uid, expectedVersion int, head types.KVMap, content any, reason string, editedAt time.Time) (*types.Message, error) {
@@ -1521,8 +1592,16 @@ func (messagesMapper) Edit(tenantID types.TenantID, topic string, seq int64, act
if err != nil {
return nil, err
}
stored, err := encryptMessageContent(&types.Message{TenantID: tenantID, Topic: topic, SeqId: seq, Content: content})
if err != nil {
return nil, err
}
storedContent := types.StoredMessageContent{Content: stored.Content, Nonce: stored.ContentNonce, KeyVersion: stored.KeyVersion}
if types.GetTopicCat(topic) == types.TopicCatGrp {
msg, err := tenantAdp.TenantGroupMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, content, reason, editedAt)
msg, err := tenantAdp.TenantGroupMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, storedContent, reason, editedAt)
if err == nil {
err = decryptMessageContent(msg)
}
if err == nil && groupMsgRuntime != nil {
if cacheErr := groupMsgRuntime.CacheHotMessage(tenantID, msg); cacheErr != nil {
logs.Warn.Printf("topic[%s]: failed to refresh edited group message cache: %v", topic, cacheErr)
@@ -1530,7 +1609,11 @@ func (messagesMapper) Edit(tenantID types.TenantID, topic string, seq int64, act
}
return msg, err
}
return tenantAdp.TenantMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, content, reason, editedAt)
msg, err := tenantAdp.TenantMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, storedContent, reason, editedAt)
if err == nil {
err = decryptMessageContent(msg)
}
return msg, err
}
type ConversationsPersistenceInterface interface {
@@ -3237,10 +3320,14 @@ func (messagesMapper) PersistPrepared(tenantID types.TenantID, msg *types.Messag
msg.ClientMsgID = clientMsgID
fromUid := types.ParseUid(msg.From)
isGroupMessage := types.GetTopicCat(msg.Topic) == types.TopicCatGrp
storedMsg, err := encryptMessageContent(msg)
if err != nil {
return nil, err
}
if isGroupMessage {
err = tenantAdp.TenantGroupMessageSave(tenantID, msg)
err = tenantAdp.TenantGroupMessageSave(tenantID, storedMsg)
} else {
err = tenantAdp.TenantMessageSave(tenantID, msg)
err = tenantAdp.TenantMessageSave(tenantID, storedMsg)
}
if err != nil {
if err == types.ErrDuplicate {
@@ -3250,6 +3337,9 @@ func (messagesMapper) PersistPrepared(tenantID types.TenantID, msg *types.Messag
return nil, dupErr
}
if duplicate != nil {
if dupErr = decryptMessageContent(duplicate); dupErr != nil {
return nil, dupErr
}
msg.SeqId = duplicate.SeqId
msg.ShardMonth = duplicate.ShardMonth
if groupMsgRuntime != nil {
@@ -3264,11 +3354,14 @@ func (messagesMapper) PersistPrepared(tenantID types.TenantID, msg *types.Messag
return nil, dupErr
}
if duplicate != nil {
if dupErr = decryptMessageContent(duplicate); dupErr != nil {
return nil, dupErr
}
copyPersistedMessage(msg, duplicate)
return &types.MessageSaveResult{SeqId: duplicate.SeqId, Duplicate: true}, nil
}
}
if retryErr := retryTenantMessageSaveAfterSeqConflict(tenantAdp, tenantID, msg, isGroupMessage); retryErr != nil {
if retryErr := retryTenantMessageSaveAfterSeqConflict(tenantAdp, tenantID, storedMsg, isGroupMessage); retryErr != nil {
if isGroupMessage && groupMsgRuntime != nil && clientMsgID != "" {
_ = groupMsgRuntime.MarkFailed(tenantID, msg.Topic, fromUid, clientMsgID)
}
@@ -3282,6 +3375,7 @@ func (messagesMapper) PersistPrepared(tenantID types.TenantID, msg *types.Messag
return nil, err
}
}
msg.SetUid(storedMsg.Uid())
if err = tenantAdp.TenantTopicUpdateOnMessage(tenantID, msg.Topic, msg); err != nil {
return nil, err
}
@@ -3363,6 +3457,8 @@ func copyPersistedMessage(dst, src *types.Message) {
dst.ClientMsgID = src.ClientMsgID
dst.Head = src.Head
dst.Content = src.Content
dst.ContentNonce = src.ContentNonce
dst.KeyVersion = src.KeyVersion
}
func PrepareMessagePersist(tenantID types.TenantID, msg *types.Message, attachmentURLs []string, readBySender bool) (*types.MessageSaveResult, error) {
@@ -3392,7 +3488,17 @@ func retryTenantMessageSaveAfterSeqConflict(tenantAdp adapter.TenantBusinessAdap
return types.ErrDuplicate
}
prevSeq := msg.SeqId
plainMsg := *msg
if err = decryptMessageContent(&plainMsg); err != nil {
return err
}
msg.SeqId = latest[0].SeqId + 1
plainMsg.SeqId = msg.SeqId
if encryptedMsg, encryptErr := encryptMessageContent(&plainMsg); encryptErr != nil {
return encryptErr
} else {
*msg = *encryptedMsg
}
if err = tenantAdp.TenantTopicUpdateOnMessage(tenantID, msg.Topic, msg); err != nil {
return err
}
@@ -3647,9 +3753,17 @@ func (messagesMapper) GetAll(tenantID types.TenantID, topic string, forUser type
return msgs, nil
}
}
return tenantAdp.TenantGroupMessageGetAll(tenantID, topic, forUser, opt)
messages, err := tenantAdp.TenantGroupMessageGetAll(tenantID, topic, forUser, opt)
if err == nil {
err = decryptMessages(messages)
}
return messages, err
}
return tenantAdp.TenantMessageGetAll(tenantID, topic, forUser, opt)
messages, err := tenantAdp.TenantMessageGetAll(tenantID, topic, forUser, opt)
if err == nil {
err = decryptMessages(messages)
}
return messages, err
}
func (messagesMapper) Search(tenantID types.TenantID, topic string, forUser types.Uid, query, messageType, cursor string, limit int) ([]types.Message, string, error) {
@@ -3663,7 +3777,40 @@ func (messagesMapper) Search(tenantID types.TenantID, topic string, forUser type
if err != nil {
return nil, "", err
}
return tenantAdp.TenantMessageSearch(tenantID, topic, forUser, query, messageType, cursor, limit)
query = strings.TrimSpace(query)
if query == "" {
messages, nextCursor, searchErr := tenantAdp.TenantMessageSearch(tenantID, topic, forUser, "", messageType, cursor, limit)
if searchErr == nil {
searchErr = decryptMessages(messages)
}
return messages, nextCursor, searchErr
}
// Encrypted content cannot be filtered by SQL. Walk visible messages in
// sequence order, decrypt them, then apply the same user-facing search.
matches := make([]types.Message, 0, limit)
nextCursor := cursor
for {
batch, batchCursor, searchErr := tenantAdp.TenantMessageSearch(tenantID, topic, forUser, "", messageType, nextCursor, 100)
if searchErr != nil {
return nil, "", searchErr
}
if searchErr = decryptMessages(batch); searchErr != nil {
return nil, "", searchErr
}
for _, msg := range batch {
if messageContentMatchesSearch(msg.Content, query) {
matches = append(matches, msg)
if len(matches) == limit {
return matches, strconv.FormatInt(msg.SeqId, 10), nil
}
}
}
if batchCursor == "" || len(batch) == 0 {
return matches, "", nil
}
nextCursor = batchCursor
}
}
func (messagesMapper) Mutations(tenantID types.TenantID, topic string, forUser types.Uid, sinceVersion int64, limit int) ([]types.MessageMutation, string, bool, int64, error) {
@@ -4661,6 +4808,7 @@ func init() {
Store = storeObj{}
Tenants = tenantsMapper{}
RegistrationConfig = registrationConfigMapper{}
RealNameConfig = realNameConfigMapper{}
TenantEntry = tenantEntryMapper{}
InternalAdminQueries = internalAdminQueryMapper{}
SecurityAudit = securityAuditMapper{}