fix tenant-scoped user search
This commit is contained in:
@@ -7201,6 +7201,10 @@ func (a *adapter) TenantUserSearch(id t.TenantID, viewer t.Uid, queryText, searc
|
|||||||
}
|
}
|
||||||
where := []string{"u.id<>?", "u.state=?"}
|
where := []string{"u.id<>?", "u.state=?"}
|
||||||
args := []any{decodeUID(viewer), t.StateOK}
|
args := []any{decodeUID(viewer), t.StateOK}
|
||||||
|
if !a.tenantScopedDB {
|
||||||
|
where = append([]string{"u.tenant_id=?"}, where...)
|
||||||
|
args = append([]any{id}, args...)
|
||||||
|
}
|
||||||
if cursor != "" {
|
if cursor != "" {
|
||||||
parsed, err := strconv.ParseInt(cursor, 10, 64)
|
parsed, err := strconv.ParseInt(cursor, 10, 64)
|
||||||
if err != nil || parsed <= 0 {
|
if err != nil || parsed <= 0 {
|
||||||
@@ -7213,6 +7217,12 @@ func (a *adapter) TenantUserSearch(id t.TenantID, viewer t.Uid, queryText, searc
|
|||||||
"LEFT JOIN im_user_privacy_settings AS privacy ON privacy.user_id=u.id",
|
"LEFT JOIN im_user_privacy_settings AS privacy ON privacy.user_id=u.id",
|
||||||
"LEFT JOIN im_user_profiles AS profile ON profile.user_id=u.id",
|
"LEFT JOIN im_user_profiles AS profile ON profile.user_id=u.id",
|
||||||
}
|
}
|
||||||
|
if !a.tenantScopedDB {
|
||||||
|
joins = []string{
|
||||||
|
"LEFT JOIN im_user_privacy_settings AS privacy ON privacy.tenant_id=u.tenant_id AND privacy.user_id=u.id",
|
||||||
|
"LEFT JOIN im_user_profiles AS profile ON profile.tenant_id=u.tenant_id AND profile.user_id=u.id",
|
||||||
|
}
|
||||||
|
}
|
||||||
addMethod := "account"
|
addMethod := "account"
|
||||||
switch searchType {
|
switch searchType {
|
||||||
case "", "account":
|
case "", "account":
|
||||||
|
|||||||
@@ -407,6 +407,69 @@ func TestTenantUserSearchMatchesUsernamePhoneAndPublicAccount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTenantUserSearchDoesNotLeakAcrossTenants(t *testing.T) {
|
||||||
|
businessAdp, ok := adp.(adapter.TenantBusinessAdapter)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("MySQL adapter does not implement TenantBusinessAdapter")
|
||||||
|
}
|
||||||
|
var tenantColumns int
|
||||||
|
if err := db.Get(&tenantColumns, `SELECT COUNT(*) FROM information_schema.columns
|
||||||
|
WHERE table_schema=DATABASE() AND table_name='im_users' AND column_name='tenant_id'`); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if tenantColumns == 0 {
|
||||||
|
t.Skip("im_users.tenant_id is missing in the current test database")
|
||||||
|
}
|
||||||
|
var tenantID types.TenantID
|
||||||
|
if err := db.Get(&tenantID, "SELECT id FROM im_tenant WHERE code='default'"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := types.TimeNow()
|
||||||
|
viewerID := now.UnixNano() & 0x3fffffffffffffff
|
||||||
|
targetID := viewerID + 1
|
||||||
|
otherTenantID := tenantID + 1000
|
||||||
|
otherTargetID := targetID + 1000
|
||||||
|
viewer := store.EncodeUid(viewerID)
|
||||||
|
target := store.EncodeUid(targetID)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
db.Exec("DELETE FROM im_user_privacy_settings WHERE user_id IN (?,?,?)", viewerID, targetID, otherTargetID)
|
||||||
|
db.Exec("DELETE FROM im_user_profiles WHERE user_id IN (?,?,?)", viewerID, targetID, otherTargetID)
|
||||||
|
db.Exec("DELETE FROM im_users WHERE id IN (?,?,?)", viewerID, targetID, otherTargetID)
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, row := range []struct {
|
||||||
|
tenantID types.TenantID
|
||||||
|
userID int64
|
||||||
|
username string
|
||||||
|
}{
|
||||||
|
{tenantID, viewerID, "viewer-scope"},
|
||||||
|
{tenantID, targetID, "shared-name"},
|
||||||
|
{otherTenantID, otherTargetID, "shared-name"},
|
||||||
|
} {
|
||||||
|
if _, err := db.Exec(
|
||||||
|
`INSERT INTO im_users(tenant_id,id,account_no,username,createdat,updatedat,state,access,phone,trusted)
|
||||||
|
VALUES(?,?,?,?,?,?,?,?,?,?)`,
|
||||||
|
row.tenantID, row.userID, row.userID%1000000, row.username, now, now, types.StateOK, `{"Auth":"JRWPAS","Anon":"N"}`, nil, `{}`); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(
|
||||||
|
`INSERT INTO im_user_privacy_settings(tenant_id,id,user_id,add_me_methods,created_at,updated_at)
|
||||||
|
VALUES(?,?,?,?,?,?)`,
|
||||||
|
row.tenantID, row.userID+1000, row.userID, `{"account":true,"phone":true,"qr":true,"group":true}`, now, now); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
users, _, err := businessAdp.TenantUserSearch(tenantID, viewer, "shared-name", "account", "", 20)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("tenant-scoped search failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(users) != 1 || users[0].Uid() != target {
|
||||||
|
t.Fatalf("tenant-scoped search returned %+v, want %s only", users, target.UserId())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ================== Create tests ================================
|
// ================== Create tests ================================
|
||||||
func TestUserCreate(t *testing.T) {
|
func TestUserCreate(t *testing.T) {
|
||||||
for _, user := range testData.Users {
|
for _, user := range testData.Users {
|
||||||
|
|||||||
Reference in New Issue
Block a user