feat: enforce friend add and curfew policies

This commit is contained in:
richjohnrambo
2026-09-18 10:55:53 +08:00
parent c6e7f6b923
commit 3a92d81fca
14 changed files with 531 additions and 42 deletions
+47 -1
View File
@@ -3635,6 +3635,10 @@ func serveIMFriendRequestCreate(wrt http.ResponseWriter, req *http.Request) {
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "invalid friend request source", nil)
return
}
if decision := imFriendAddPolicyDecision(principal.TenantID, "", principal.Uid, source); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
privacy, err := store.Profiles.GetPrivacy(principal.TenantID, target)
if err != nil {
writeIMRelationError(wrt, req, err)
@@ -4048,10 +4052,14 @@ func serveIMGroupMembers(wrt http.ResponseWriter, req *http.Request, topic strin
return
}
remarks := groupMemberRemarkMap(principal.TenantID, principal.Uid, members)
hideFriendEntrypoint := !imFriendGlobalDisabledDecision(principal.TenantID, "", "group").Allowed
items := make([]map[string]any, 0, limit)
for _, member := range members {
access := groupMemberAccess(member)
item := groupBusinessMemberPayload(member, remarks[member.UserID])
if hideFriendEntrypoint && member.UserID != principal.Uid {
maskIMGroupMemberProfileEntrypoint(item)
}
if eligibleRole != "" {
eligible, reason := groupRoleEligibility(selfAccess, access, eligibleRole)
item["eligible"] = eligible
@@ -5467,6 +5475,12 @@ func serveIMGroupMemberProfile(wrt http.ResponseWriter, req *http.Request, topic
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "user_id"})
return
}
if memberID != principal.Uid {
if decision := imFriendGlobalDisabledDecision(principal.TenantID, "", "group"); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
}
selfSub, err := store.Subs.Get(principal.TenantID, topic, principal.Uid, false)
if err != nil {
writeIMError(wrt, req, http.StatusInternalServerError, 90000, imReasonInternal, "internal error", nil)
@@ -5519,6 +5533,10 @@ func serveIMMeQRCodes(wrt http.ResponseWriter, req *http.Request) {
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "purpose"})
return
}
if decision := imFriendGlobalDisabledDecision(principal.TenantID, "", "qr"); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
expiresIn := body.ExpiresInSeconds
if expiresIn <= 0 {
expiresIn = imQRCodeDefaultExpiresInSeconds
@@ -5624,6 +5642,10 @@ func serveIMQRCodesResolve(wrt http.ResponseWriter, req *http.Request) {
writeIMRelationError(wrt, req, err)
return
}
if decision := imFriendGlobalDisabledDecision(principal.TenantID, "", "qr"); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
canAdd := ctx.Relationship == types.RelationshipNone && imPrivacyAllowsAdd(privacy, "qr")
action := imRelationshipAction(ctx.Relationship)
if ctx.Relationship == types.RelationshipNone && !canAdd {
@@ -5665,8 +5687,14 @@ func serveIMUserSearch(wrt http.ResponseWriter, req *http.Request) {
return
}
payload := make([]map[string]any, 0, len(items))
friendSearchAllowed := imFriendGlobalDisabledDecision(principal.TenantID, "", "search").Allowed
for _, item := range items {
payload = append(payload, userSearchItemPayload(item))
data := userSearchItemPayload(item)
if !friendSearchAllowed && item.Relationship == types.RelationshipNone {
data["can_add"] = false
data["action"] = "none"
}
payload = append(payload, data)
}
writeIMSuccess(wrt, req, map[string]any{"items": payload, "next_cursor": nextCursor})
}
@@ -5738,6 +5766,12 @@ func serveIMUserProfile(wrt http.ResponseWriter, req *http.Request, identifier s
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "profile is private", nil)
return
}
if ctx.Relationship == types.RelationshipNone {
if decision := imFriendGlobalDisabledDecision(principal.TenantID, "", "profile"); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
}
writeIMSuccess(wrt, req, userProfilePayload(user, ctx))
}
@@ -5795,6 +5829,10 @@ func serveIMUserQRCodes(wrt http.ResponseWriter, req *http.Request, identifier s
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", nil)
return
}
if decision := imFriendGlobalDisabledDecision(principal.TenantID, "", "qr"); !decision.Allowed {
writeIMPolicyDecision(wrt, req, decision)
return
}
}
expiresIn := body.ExpiresInSeconds
if expiresIn <= 0 {
@@ -6932,6 +6970,14 @@ func groupBusinessMemberPayload(member types.GroupMember, remark string) map[str
return data
}
func maskIMGroupMemberProfileEntrypoint(data map[string]any) {
for _, key := range []string{"avatar_file_id", "public_account", "signature", "gender", "region"} {
delete(data, key)
}
data["can_add"] = false
data["action"] = "none"
}
func copyUserNickname(data map[string]any, public any) {
var fields map[string]any
if value, ok := public.(map[string]any); ok {