feat(server): 为内部管理查询添加租户隔离支持

实现共库模式下的租户数据隔离,新增 TenantColumn 字段标识查询表的租户列,添加 internalAdminTenantCondition 函数生成租户过滤条件。修改所有内部管理查询的构建逻辑,在列表和详情查询中自动注入租户条件,防止跨租户数据泄漏。同时更新所有资源查询规格,配置对应表的租户列。
This commit is contained in:
Austin
2026-09-23 12:11:26 +08:00
parent a0d0bf574e
commit b8b21d3548
2 changed files with 102 additions and 45 deletions
+31 -8
View File
@@ -467,7 +467,12 @@ func serveInternalUserQuery(wrt http.ResponseWriter, req *http.Request, tenantID
}
if spec, ok := internalAdminQuerySpec("users"); ok {
query := spec.Select + " " + spec.From + " WHERE u.id=?"
if row, err := store.InternalAdminQueries.QueryOne(tenantID, query, []any{userDBID}); err == nil {
args := []any{userDBID}
if cond, extra := internalAdminTenantCondition(spec, tenantID); cond != "" {
query += cond
args = append(args, extra...)
}
if row, err := store.InternalAdminQueries.QueryOne(tenantID, query, args); err == nil {
writeIMSuccess(wrt, req, normalizeInternalAdminRowForAPI(row))
return
} else {
@@ -508,7 +513,12 @@ func serveInternalGroupQuery(wrt http.ResponseWriter, req *http.Request, tenantI
}
if spec, ok := internalAdminQuerySpec("groups"); ok {
query := spec.Select + " " + spec.From + " WHERE t.name=?"
if row, err := store.InternalAdminQueries.QueryOne(tenantID, query, []any{topic}); err == nil {
args := []any{topic}
if cond, extra := internalAdminTenantCondition(spec, tenantID); cond != "" {
query += cond
args = append(args, extra...)
}
if row, err := store.InternalAdminQueries.QueryOne(tenantID, query, args); err == nil {
writeIMSuccess(wrt, req, normalizeInternalAdminRowForAPI(row))
return
}
@@ -529,9 +539,10 @@ func serveInternalGroupMembersQuery(wrt http.ResponseWriter, req *http.Request,
spec := &internalQuerySpec{
Select: `SELECT gm.id,gm.topic,gm.user_id,u.state,p.nickname,p.public_account,p.avatar_file_id,p.signature,p.gender,p.region,s.modewant AS mode_want,s.modegiven AS mode_given,s.recvseqid AS recv_seq_id,s.readseqid AS read_seq_id,s.delid AS del_id,
gm.role,gm.member_status,gm.nickname AS group_nickname,gm.pinned,gm.mute_notifications,gm.join_source,gm.joined_by,gm.joined_at,gm.left_at,gm.removed_by,gm.version,gm.created_at,gm.updated_at`,
From: "FROM im_group_members AS gm LEFT JOIN im_users AS u ON u.id=gm.user_id LEFT JOIN im_user_profiles AS p ON p.user_id=gm.user_id LEFT JOIN im_subscriptions AS s ON s.topic=gm.topic AND s.userid=gm.user_id",
DefaultSort: "ORDER BY gm.id DESC",
ScopeColumn: "gm.topic",
From: "FROM im_group_members AS gm LEFT JOIN im_users AS u ON u.id=gm.user_id LEFT JOIN im_user_profiles AS p ON p.user_id=gm.user_id LEFT JOIN im_subscriptions AS s ON s.topic=gm.topic AND s.userid=gm.user_id",
DefaultSort: "ORDER BY gm.id DESC",
ScopeColumn: "gm.topic",
TenantColumn: "gm.tenant_id",
Filters: []internalQueryFilter{
{"user_id", "gm.user_id", "=", "uid"},
{"role", "gm.role", "=", "string"},
@@ -540,7 +551,7 @@ func serveInternalGroupMembersQuery(wrt http.ResponseWriter, req *http.Request,
{"eligible_role", "gm.role", "=", "string"},
},
}
query, countQuery, args, page, perPage, err := buildInternalAdminListQuery(req, spec, topic)
query, countQuery, args, page, perPage, err := buildInternalAdminListQuery(req, spec, topic, tenantID)
if err == nil {
if result, queryErr := store.InternalAdminQueries.Query(tenantID, query, countQuery, args, page, perPage); queryErr == nil {
writeIMSuccess(wrt, req, map[string]any{
@@ -606,7 +617,13 @@ func serveInternalMessageQuery(wrt http.ResponseWriter, req *http.Request, tenan
}
}
spec.Select += ",m.head,JSON_UNQUOTE(m.content) AS content"
row, err := store.InternalAdminQueries.QueryOne(tenantID, spec.Select+" "+spec.From+" WHERE "+where, []any{topic, seq})
query := spec.Select + " " + spec.From + " WHERE " + where
args := []any{topic, seq}
if cond, extra := internalAdminTenantCondition(spec, tenantID); cond != "" {
query += cond
args = append(args, extra...)
}
row, err := store.InternalAdminQueries.QueryOne(tenantID, query, args)
if err != nil {
writeInternalQueryError(wrt, req, err)
return
@@ -633,7 +650,13 @@ func serveInternalFileQuery(wrt http.ResponseWriter, req *http.Request, tenantID
return
}
spec, _ := internalAdminQuerySpec("files")
row, err := store.InternalAdminQueries.QueryOne(tenantID, spec.Select+" "+spec.From+" WHERE id=?", []any{fileDBID})
query := spec.Select + " " + spec.From + " WHERE id=?"
args := []any{fileDBID}
if cond, extra := internalAdminTenantCondition(spec, tenantID); cond != "" {
query += cond
args = append(args, extra...)
}
row, err := store.InternalAdminQueries.QueryOne(tenantID, query, args)
if err != nil {
writeInternalQueryError(wrt, req, err)
return