feat: 接入消息加密与实名认证能力
This commit is contained in:
+159
-11
@@ -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{}
|
||||
|
||||
Reference in New Issue
Block a user