mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-01-19 22:27:59 +01:00
subnet bans
This commit is contained in:
parent
f044ab6d3e
commit
304d5486d6
9 changed files with 241 additions and 8 deletions
|
@ -145,9 +145,9 @@ defmodule Philomena.Bans do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_subnet(attrs \\ %{}) do
|
def create_subnet(creator, attrs \\ %{}) do
|
||||||
%Subnet{}
|
%Subnet{banning_user_id: creator.id}
|
||||||
|> Subnet.changeset(attrs)
|
|> Subnet.save_changeset(attrs)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ defmodule Philomena.Bans do
|
||||||
"""
|
"""
|
||||||
def update_subnet(%Subnet{} = subnet, attrs) do
|
def update_subnet(%Subnet{} = subnet, attrs) do
|
||||||
subnet
|
subnet
|
||||||
|> Subnet.changeset(attrs)
|
|> Subnet.save_changeset(attrs)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule Philomena.Bans.Subnet do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Philomena.Users.User
|
alias Philomena.Users.User
|
||||||
|
alias RelativeDate.Parser
|
||||||
|
|
||||||
schema "subnet_bans" do
|
schema "subnet_bans" do
|
||||||
belongs_to :banning_user, User
|
belongs_to :banning_user, User
|
||||||
|
@ -14,13 +15,47 @@ defmodule Philomena.Bans.Subnet do
|
||||||
field :specification, EctoNetwork.INET
|
field :specification, EctoNetwork.INET
|
||||||
field :generated_ban_id, :string
|
field :generated_ban_id, :string
|
||||||
|
|
||||||
|
field :until, :string, virtual: true
|
||||||
|
|
||||||
timestamps(inserted_at: :created_at)
|
timestamps(inserted_at: :created_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def changeset(subnet, attrs) do
|
def changeset(subnet_ban, attrs) do
|
||||||
subnet
|
subnet_ban
|
||||||
|> cast(attrs, [])
|
|> cast(attrs, [])
|
||||||
|> validate_required([])
|
|> populate_until()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def save_changeset(subnet_ban, attrs) do
|
||||||
|
subnet_ban
|
||||||
|
|> cast(attrs, [:reason, :note, :enabled, :specification, :until])
|
||||||
|
|> populate_valid_until()
|
||||||
|
|> put_ban_id()
|
||||||
|
|> validate_required([:reason, :enabled, :specification, :valid_until])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp populate_until(%{data: data} = changeset) do
|
||||||
|
put_change(changeset, :until, to_string(data.valid_until))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp populate_valid_until(changeset) do
|
||||||
|
changeset
|
||||||
|
|> get_field(:until)
|
||||||
|
|> Parser.parse()
|
||||||
|
|> case do
|
||||||
|
{:ok, time} ->
|
||||||
|
change(changeset, valid_until: time)
|
||||||
|
|
||||||
|
{:error, _err} ->
|
||||||
|
add_error(changeset, :until, "is not a valid absolute or relative date and time")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_ban_id(%{data: %{generated_ban_id: nil}} = changeset) do
|
||||||
|
ban_id = Base.encode16(:crypto.strong_rand_bytes(3))
|
||||||
|
|
||||||
|
put_change(changeset, :generated_ban_id, "S#{ban_id}")
|
||||||
|
end
|
||||||
|
defp put_ban_id(changeset), do: changeset
|
||||||
end
|
end
|
||||||
|
|
100
lib/philomena_web/controllers/admin/subnet_ban_controller.ex
Normal file
100
lib/philomena_web/controllers/admin/subnet_ban_controller.ex
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.SubnetBanController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias Philomena.Bans.Subnet, as: SubnetBan
|
||||||
|
alias Philomena.Bans
|
||||||
|
alias Philomena.Repo
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
plug :verify_authorized
|
||||||
|
plug :load_resource, model: SubnetBan, only: [:edit, :update, :delete]
|
||||||
|
|
||||||
|
def index(conn, %{"q" => q}) when is_binary(q) do
|
||||||
|
like_q = "%#{q}%"
|
||||||
|
|
||||||
|
SubnetBan
|
||||||
|
|> where([sb],
|
||||||
|
sb.generated_ban_id == ^q
|
||||||
|
or fragment("to_tsvector(?) @@ plainto_tsquery(?)", sb.reason, ^q)
|
||||||
|
or fragment("to_tsvector(?) @@ plainto_tsquery(?)", sb.note, ^q)
|
||||||
|
)
|
||||||
|
|> load_bans(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index(conn, %{"ip" => ip}) when is_binary(ip) do
|
||||||
|
{:ok, ip} = EctoNetwork.INET.cast(ip)
|
||||||
|
|
||||||
|
SubnetBan
|
||||||
|
|> where([sb], fragment("? >>= ?", sb.specification, ^ip))
|
||||||
|
|> load_bans(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
load_bans(SubnetBan, conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(conn, %{"specification" => ip}) do
|
||||||
|
{:ok, ip} = EctoNetwork.INET.cast(ip)
|
||||||
|
changeset = Bans.change_subnet(%SubnetBan{specification: ip})
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(conn, _params) do
|
||||||
|
changeset = Bans.change_subnet(%SubnetBan{})
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(conn, %{"subnet" => subnet_ban_params}) do
|
||||||
|
case Bans.create_subnet(conn.assigns.current_user, subnet_ban_params) do
|
||||||
|
{:ok, _subnet_ban} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "User was successfully banned.")
|
||||||
|
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit(conn, _params) do
|
||||||
|
changeset = Bans.change_subnet(conn.assigns.subnet)
|
||||||
|
render(conn, "edit.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(conn, %{"subnet" => subnet_ban_params}) do
|
||||||
|
case Bans.update_subnet(conn.assigns.subnet, subnet_ban_params) do
|
||||||
|
{:ok, _subnet_ban} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Subnet ban successfully updated.")
|
||||||
|
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
render(conn, "edit.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(conn, _params) do
|
||||||
|
{:ok, _subnet_ban} = Bans.delete_subnet(conn.assigns.subnet)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Subnet ban successfully deleted.")
|
||||||
|
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_bans(queryable, conn) do
|
||||||
|
subnet_bans =
|
||||||
|
queryable
|
||||||
|
|> order_by(desc: :created_at)
|
||||||
|
|> preload(:banning_user)
|
||||||
|
|> Repo.paginate(conn.assigns.scrivener)
|
||||||
|
|
||||||
|
render(conn, "index.html", layout_class: "layout--wide", subnet_bans: subnet_bans)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp verify_authorized(conn, _opts) do
|
||||||
|
case Canada.Can.can?(conn.assigns.current_user, :index, SubnetBan) do
|
||||||
|
true -> conn
|
||||||
|
false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,22 @@
|
||||||
|
= form_for @changeset, @action, fn f ->
|
||||||
|
= if @changeset.action do
|
||||||
|
.alert.alert-danger
|
||||||
|
p Oops, something went wrong! Please check the errors below.
|
||||||
|
|
||||||
|
.field
|
||||||
|
=> label f, :specification, "Specification:"
|
||||||
|
= text_input f, :specification, class: "input", placeholder: "Specification", required: true
|
||||||
|
|
||||||
|
.field
|
||||||
|
=> label f, :reason, "Reason (shown to the banned user, and to staff on the user's profile page):"
|
||||||
|
= text_input f, :reason, class: "input input--wide", placeholder: "Reason", required: true
|
||||||
|
|
||||||
|
.field
|
||||||
|
=> label f, :note, "Admin-only note:"
|
||||||
|
= text_input f, :note, class: "input input--wide", placeholder: "Note"
|
||||||
|
|
||||||
|
.field
|
||||||
|
=> label f, :until, "End time relative to now, in simple English (e.g. \"1 week from now\"):"
|
||||||
|
= text_input f, :until, class: "input input--wide", placeholder: "Until", required: true
|
||||||
|
|
||||||
|
= submit "Save Ban", class: "button"
|
|
@ -0,0 +1,6 @@
|
||||||
|
h1 Editing ban
|
||||||
|
|
||||||
|
= render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: Routes.admin_subnet_ban_path(@conn, :update, @subnet), conn: @conn
|
||||||
|
|
||||||
|
br
|
||||||
|
= link "Back", to: Routes.admin_subnet_ban_path(@conn, :index)
|
|
@ -0,0 +1,52 @@
|
||||||
|
h1 Subnet Bans
|
||||||
|
|
||||||
|
- route = fn p -> Routes.admin_subnet_ban_path(@conn, :index, p) end
|
||||||
|
- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @subnet_bans, route: route
|
||||||
|
|
||||||
|
.block
|
||||||
|
.block__header
|
||||||
|
= pagination
|
||||||
|
|
||||||
|
.block__content
|
||||||
|
table.table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Specification
|
||||||
|
th Created
|
||||||
|
th Expires
|
||||||
|
th Reason/Note
|
||||||
|
th Ban ID
|
||||||
|
th Options
|
||||||
|
|
||||||
|
tbody
|
||||||
|
= for ban <- @subnet_bans do
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
= link ban.specification, to: Routes.ip_profile_path(@conn, :show, to_string(ban.specification))
|
||||||
|
|
||||||
|
td
|
||||||
|
=> pretty_time ban.created_at
|
||||||
|
= user_abbrv @conn, ban.banning_user
|
||||||
|
|
||||||
|
td class=ban_row_class(ban)
|
||||||
|
= pretty_time ban.valid_until
|
||||||
|
|
||||||
|
td
|
||||||
|
= ban.reason
|
||||||
|
|
||||||
|
= if present?(ban.note) do
|
||||||
|
p.block.block--fixed
|
||||||
|
em
|
||||||
|
' Note:
|
||||||
|
= ban.note
|
||||||
|
|
||||||
|
td
|
||||||
|
= ban.generated_ban_id
|
||||||
|
|
||||||
|
td
|
||||||
|
=> link "Edit", to: Routes.admin_subnet_ban_path(@conn, :edit, ban)
|
||||||
|
' •
|
||||||
|
=> link "Destroy", to: Routes.admin_subnet_ban_path(@conn, :delete, ban), data: [confirm: "Are you really, really sure?", method: "delete"]
|
||||||
|
|
||||||
|
.block__header.block__header--light
|
||||||
|
= pagination
|
|
@ -0,0 +1,5 @@
|
||||||
|
h1 New Subnet Ban
|
||||||
|
= render PhilomenaWeb.Admin.SubnetBanView, "_form.html", changeset: @changeset, action: Routes.admin_subnet_ban_path(@conn, :create), conn: @conn
|
||||||
|
|
||||||
|
br
|
||||||
|
= link "Back", to: Routes.admin_subnet_ban_path(@conn, :index)
|
14
lib/philomena_web/views/admin/subnet_ban_view.ex
Normal file
14
lib/philomena_web/views/admin/subnet_ban_view.ex
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.SubnetBanView do
|
||||||
|
use PhilomenaWeb, :view
|
||||||
|
|
||||||
|
import PhilomenaWeb.ProfileView, only: [user_abbrv: 2]
|
||||||
|
|
||||||
|
defp ban_row_class(%{valid_until: until, enabled: enabled}) do
|
||||||
|
now = DateTime.utc_now()
|
||||||
|
|
||||||
|
case enabled and DateTime.diff(until, now) > 0 do
|
||||||
|
true -> "success"
|
||||||
|
_false -> "danger"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -63,7 +63,6 @@ defmodule PhilomenaWeb.ReportView do
|
||||||
do: link "User '#{r.name}'", to: Routes.profile_path(conn, :show, r)
|
do: link "User '#{r.name}'", to: Routes.profile_path(conn, :show, r)
|
||||||
|
|
||||||
def link_to_reported_thing(_conn, report) do
|
def link_to_reported_thing(_conn, report) do
|
||||||
IO.inspect report
|
|
||||||
"Reported item permanently destroyed."
|
"Reported item permanently destroyed."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue