Skip to content

Commit 4e802f7

Browse files
committed
feat: add banned_until
1 parent ea91d69 commit 4e802f7

File tree

4 files changed

+80
-5
lines changed

4 files changed

+80
-5
lines changed

lib/supavisor/tenants/tenant.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ defmodule Supavisor.Tenants.Tenant do
3737
field(:jit_api_url, :string)
3838
field(:banned_at, :utc_datetime)
3939
field(:ban_reason, :string)
40+
field(:banned_until, :utc_datetime)
4041

4142
has_many(:users, User,
4243
foreign_key: :tenant_external_id,
@@ -137,12 +138,12 @@ defmodule Supavisor.Tenants.Tenant do
137138
@doc false
138139
def ban_changeset(tenant, %{"banned" => "true"} = params) do
139140
tenant
140-
|> cast(params, [:ban_reason])
141+
|> cast(params, [:ban_reason, :banned_until])
141142
|> validate_required([:ban_reason])
142143
|> put_change(:banned_at, DateTime.utc_now() |> DateTime.truncate(:second))
143144
end
144145

145146
def unban_changeset(tenant, %{"banned" => "false"}) do
146-
change(tenant, banned_at: nil, ban_reason: nil)
147+
change(tenant, banned_at: nil, ban_reason: nil, banned_until: nil)
147148
end
148149
end

lib/supavisor_web/open_api_schemas.ex

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ defmodule SupavisorWeb.OpenApiSchemas do
8686
nullable: true
8787
},
8888
ban_reason: %Schema{type: :string, description: "Reason for ban", nullable: true},
89+
banned_until: %Schema{
90+
type: :string,
91+
format: :date_time,
92+
description: "Ban expiry timestamp",
93+
nullable: true
94+
},
8995
inserted_at: %Schema{type: :string, format: :date_time, readOnly: true},
9096
updated_at: %Schema{type: :string, format: :date_time, readOnly: true}
9197
},
@@ -106,6 +112,7 @@ defmodule SupavisorWeb.OpenApiSchemas do
106112
allow_list: ["0.0.0.0/0", "::/0"],
107113
banned_at: "2026-01-01T00:00:00Z",
108114
ban_reason: "abuse",
115+
banned_until: "2926-04-01T00:00:00Z",
109116
users: [
110117
%{
111118
id: "b1024a4c-4eb4-4c64-8f49-c8a46c2b2e16",
@@ -394,10 +401,19 @@ defmodule SupavisorWeb.OpenApiSchemas do
394401
ban_reason: %Schema{
395402
type: :string,
396403
description: "Reason for the ban (required when banned is true)"
404+
},
405+
banned_until: %Schema{
406+
type: :string,
407+
format: :date_time,
408+
description: "Optional ban expiry timestamp"
397409
}
398410
},
399411
required: [:banned],
400-
example: %{banned: true, ban_reason: "Acceptable use policy violation"}
412+
example: %{
413+
banned: true,
414+
ban_reason: "Acceptable use policy violation",
415+
banned_until: "2026-01-01T00:00:00Z"
416+
}
401417
})
402418

403419
def params, do: {"ToggleTenant Ban Params", "application/json", __MODULE__}

priv/repo/migrations/20260323000000_add_tenant_ban_fields.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule Supavisor.Repo.Migrations.AddTenantBanFields do
55
alter table(:tenants, prefix: "_supavisor") do
66
add(:banned_at, :utc_datetime, null: true)
77
add(:ban_reason, :string, null: true)
8+
add(:banned_until, :utc_datetime, null: true)
89
end
910
end
1011
end

test/supavisor_web/controllers/tenant_controller_test.exs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ defmodule SupavisorWeb.TenantControllerTest do
598598
data: %{
599599
external_id: ^external_id,
600600
banned_at: banned_at,
601-
ban_reason: "abuse"
601+
ban_reason: "abuse",
602+
banned_until: nil
602603
}
603604
} =
604605
conn
@@ -666,14 +667,70 @@ defmodule SupavisorWeb.TenantControllerTest do
666667

667668
# Then unban
668669
assert %{
669-
data: %{external_id: ^external_id, banned_at: nil, ban_reason: nil}
670+
data: %{
671+
external_id: ^external_id,
672+
banned_at: nil,
673+
ban_reason: nil,
674+
banned_until: nil
675+
}
670676
} =
671677
conn
672678
|> patch(~p"/api/tenants/#{external_id}", %{banned: "false"})
673679
|> json_response(200)
674680
|> assert_schema("TenantData")
675681
end
676682

683+
test "banning with banned_until persists the value", %{
684+
conn: conn,
685+
tenant: %Tenant{external_id: external_id}
686+
} do
687+
banned_until = "2099-12-31T00:00:00Z"
688+
689+
assert %{data: %{banned_until: ^banned_until}} =
690+
conn
691+
|> patch(~p"/api/tenants/#{external_id}", %{
692+
banned: "true",
693+
ban_reason: "abuse",
694+
banned_until: banned_until
695+
})
696+
|> json_response(200)
697+
|> assert_schema("TenantData")
698+
end
699+
700+
test "unbanning clears banned_until", %{
701+
conn: conn,
702+
tenant: %Tenant{external_id: external_id}
703+
} do
704+
conn
705+
|> patch(~p"/api/tenants/#{external_id}", %{
706+
banned: "true",
707+
ban_reason: "abuse",
708+
banned_until: "2099-12-31T00:00:00Z"
709+
})
710+
|> json_response(200)
711+
712+
assert %{data: %{banned_until: nil}} =
713+
conn
714+
|> patch(~p"/api/tenants/#{external_id}", %{banned: "false"})
715+
|> json_response(200)
716+
|> assert_schema("TenantData")
717+
end
718+
719+
test "returns 422 when banned_until is not a valid datetime", %{
720+
conn: conn,
721+
tenant: %Tenant{external_id: external_id}
722+
} do
723+
assert %{"errors" => _} =
724+
conn
725+
|> patch(~p"/api/tenants/#{external_id}", %{
726+
banned: "true",
727+
ban_reason: "abuse",
728+
banned_until: "not-a-date"
729+
})
730+
|> json_response(422)
731+
|> assert_schema("UnprocessablyEntity")
732+
end
733+
677734
test "includes banned_at and ban_reason in GET response after ban", %{
678735
conn: conn,
679736
tenant: %Tenant{external_id: external_id}

0 commit comments

Comments
 (0)