fix(moderation): администратор инстанса тоже не может модерировать себя

Проверка иерархии пропускала все запреты для администратора инстанса, включая
запрет на модерацию себя: он мог забанить или исключить самого себя и потерять
доступ к серверу (поймано при проверке интерфейса на стенде — самобан снял
участие в «Главном сервере»).

Теперь проверка `targetID == actor.ID` выполняется до обхода иерархии, для
администратора инстанса остаются доступны любые другие цели. Регрессионный
тест: самобан и самоисключение администратора инстанса дают 403.
This commit is contained in:
2026-09-20 22:57:45 +03:00
parent 8613989b21
commit dff4a25332
2 changed files with 29 additions and 3 deletions
+24
View File
@@ -209,6 +209,30 @@ func TestMemberTimeoutBlocksSending(t *testing.T) {
}
}
// TestInstanceAdminCannotModerateSelf: администратор инстанса обходит иерархию
// ролей, но не запрет на модерацию себя — иначе он исключает себя из сервера.
func TestInstanceAdminCannotModerateSelf(t *testing.T) {
srv, owner, _, guildID, _ := banFixture(t)
promoteAdmin(t, srv, "ban-owner@example.com")
self := doJSON(t, srv, http.MethodGet, "/api/v1/users/@me", "", owner)
ownerID := decodeResponse[struct {
User struct {
ID string `json:"id"`
} `json:"user"`
}](t, self).User.ID
ban := doJSON(t, srv, http.MethodPut, "/api/v1/guilds/"+guildID+"/bans/"+ownerID,
`{"reason":"сам себя"}`, owner)
if ban.Code != http.StatusForbidden {
t.Fatalf("самобан администратора инстанса = %d, ожидался 403", ban.Code)
}
kick := doJSON(t, srv, http.MethodDelete, "/api/v1/guilds/"+guildID+"/members/"+ownerID, "", owner)
if kick.Code != http.StatusForbidden {
t.Fatalf("самоисключение администратора инстанса = %d, ожидался 403", kick.Code)
}
}
func TestTimeoutCannotBeSetOnSelf(t *testing.T) {
srv, owner, _, guildID, _ := banFixture(t) // участник не нужен: проверяем себя
self := doJSON(t, srv, http.MethodGet, "/api/v1/users/@me", "", owner)