Skip to content

Commit ef642e2

Browse files
mentelsclaude
andcommitted
feat: respect banned_until expiry in tenant ban check
check_tenant_not_banned/1 now allows connections when banned_until is set and already in the past (time-limited ban expired). Adds tests for permanent ban, future expiry, and past expiry cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 10bc55b commit ef642e2

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

lib/supavisor/client_handler/checks.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ defmodule Supavisor.ClientHandler.Checks do
1010

1111
def check_tenant_not_banned(%{tenant: %{banned_at: nil}}), do: :ok
1212

13+
def check_tenant_not_banned(%{tenant: %{banned_until: banned_until, ban_reason: reason}})
14+
when not is_nil(banned_until) do
15+
if DateTime.compare(banned_until, DateTime.utc_now()) == :lt do
16+
:ok
17+
else
18+
{:error, %TenantBannedError{ban_reason: reason}}
19+
end
20+
end
21+
1322
def check_tenant_not_banned(%{tenant: %{ban_reason: reason}}) do
1423
{:error, %TenantBannedError{ban_reason: reason}}
1524
end

test/supavisor/client_handler_test.exs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,28 @@ defmodule Supavisor.ClientHandlerTest do
8383
assert :ok = Checks.check_tenant_not_banned(info)
8484
end
8585

86-
test "returns TenantBannedError when tenant has banned_at set" do
87-
info = %{tenant: %Tenant{banned_at: ~U[2026-01-01 00:00:00Z], ban_reason: "abuse"}}
86+
test "returns TenantBannedError for a permanent ban (banned_until nil)" do
87+
info = %{tenant: %Tenant{banned_at: ~U[2026-01-01 00:00:00Z], ban_reason: "abuse", banned_until: nil}}
8888

8989
assert {:error, %TenantBannedError{ban_reason: "abuse"}} =
9090
Checks.check_tenant_not_banned(info)
9191
end
9292

93+
test "returns TenantBannedError when banned_until is in the future" do
94+
future = DateTime.utc_now() |> DateTime.add(3600, :second)
95+
info = %{tenant: %Tenant{banned_at: ~U[2026-01-01 00:00:00Z], ban_reason: "abuse", banned_until: future}}
96+
97+
assert {:error, %TenantBannedError{ban_reason: "abuse"}} =
98+
Checks.check_tenant_not_banned(info)
99+
end
100+
101+
test "returns :ok when banned_until is in the past (ban expired)" do
102+
past = DateTime.utc_now() |> DateTime.add(-3600, :second)
103+
info = %{tenant: %Tenant{banned_at: ~U[2026-01-01 00:00:00Z], ban_reason: "abuse", banned_until: past}}
104+
105+
assert :ok = Checks.check_tenant_not_banned(info)
106+
end
107+
93108
test "TenantBannedError produces a FATAL postgres error message" do
94109
error = %TenantBannedError{ban_reason: "billing"}
95110
postgres_error = TenantBannedError.postgres_error(error)

0 commit comments

Comments
 (0)