From 06e334121f9776737a825cc83751ba82a565fb3d Mon Sep 17 00:00:00 2001 From: Even <412401474@qq.com> Date: Thu, 24 Sep 2026 00:54:12 +0800 Subject: [PATCH] fix(user): allow unblocking users promoted to admin (#39192) Fixes #39189. `IsUserBlockedBy` intentionally treats admin users as not blocked, but `CanUnblockUser` was also using it to determine whether a blocking relationship exists. If a previously blocked user is later promoted to admin, the existing `user_blocking` record remains but can no longer be removed. This change separates those two concerns by adding `HasBlocking` for checking the persisted blocking relationship. `CanUnblockUser` uses that relationship check while `IsUserBlockedBy` keeps its existing admin-user behavior. A regression test verifies that an admin is still not considered blocked while an existing blocking relationship can still be unblocked. --- models/user/block.go | 15 ++++++++++----- services/user/block.go | 2 +- services/user/block_test.go | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/models/user/block.go b/models/user/block.go index e4e4756cc0f..abf4daa57d0 100644 --- a/models/user/block.go +++ b/models/user/block.go @@ -45,15 +45,20 @@ func UpdateBlockingNote(ctx context.Context, id int64, note string) error { } func IsUserBlockedBy(ctx context.Context, blockee *User, blockerIDs ...int64) bool { - if len(blockerIDs) == 0 { - return false - } - if blockee.IsAdmin { return false } - cond := builder.Eq{"user_blocking.blockee_id": blockee.ID}. + return HasBlocking(ctx, blockee.ID, blockerIDs...) +} + +// HasBlocking reports whether a blocking relationship exists regardless of the blockee's admin status. +func HasBlocking(ctx context.Context, blockeeID int64, blockerIDs ...int64) bool { + if len(blockerIDs) == 0 { + return false + } + + cond := builder.Eq{"user_blocking.blockee_id": blockeeID}. And(builder.In("user_blocking.blocker_id", blockerIDs)) has, _ := db.GetEngine(ctx).Where(cond).Exist(&Blocking{}) diff --git a/services/user/block.go b/services/user/block.go index 48e6ed353d8..1498ba100c9 100644 --- a/services/user/block.go +++ b/services/user/block.go @@ -50,7 +50,7 @@ func CanUnblockUser(ctx context.Context, doer, blocker, blockee *user_model.User return false } - if !user_model.IsUserBlockedBy(ctx, blockee, blocker.ID) { + if !user_model.HasBlocking(ctx, blockee.ID, blocker.ID) { return false } diff --git a/services/user/block_test.go b/services/user/block_test.go index dc90ddd7e2c..f97d19ebf7d 100644 --- a/services/user/block_test.go +++ b/services/user/block_test.go @@ -62,4 +62,8 @@ func TestCanUnblockUser(t *testing.T) { assert.True(t, CanUnblockUser(t.Context(), user1, user2, user29)) assert.True(t, CanUnblockUser(t.Context(), user2, user2, user29)) assert.True(t, CanUnblockUser(t.Context(), user1, org17, user28)) + // Existing block can still be removed after the blockee becomes an admin. + user29.IsAdmin = true + assert.False(t, user_model.IsUserBlockedBy(t.Context(), user29, user2.ID)) + assert.True(t, CanUnblockUser(t.Context(), user2, user2, user29)) }