mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-24 04:27:59 +01:00
fingerprint bans
This commit is contained in:
parent
642ded69a5
commit
fefb91644b
9 changed files with 238 additions and 8 deletions
|
@ -49,9 +49,9 @@ defmodule Philomena.Bans do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_fingerprint(attrs \\ %{}) do
|
def create_fingerprint(creator, attrs \\ %{}) do
|
||||||
%Fingerprint{}
|
%Fingerprint{banning_user_id: creator.id}
|
||||||
|> Fingerprint.changeset(attrs)
|
|> Fingerprint.save_changeset(attrs)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ defmodule Philomena.Bans do
|
||||||
"""
|
"""
|
||||||
def update_fingerprint(%Fingerprint{} = fingerprint, attrs) do
|
def update_fingerprint(%Fingerprint{} = fingerprint, attrs) do
|
||||||
fingerprint
|
fingerprint
|
||||||
|> Fingerprint.changeset(attrs)
|
|> Fingerprint.save_changeset(attrs)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule Philomena.Bans.Fingerprint do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Philomena.Users.User
|
alias Philomena.Users.User
|
||||||
|
alias RelativeDate.Parser
|
||||||
|
|
||||||
schema "fingerprint_bans" do
|
schema "fingerprint_bans" do
|
||||||
belongs_to :banning_user, User
|
belongs_to :banning_user, User
|
||||||
|
@ -14,13 +15,47 @@ defmodule Philomena.Bans.Fingerprint do
|
||||||
field :fingerprint, :string
|
field :fingerprint, :string
|
||||||
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(fingerprint, attrs) do
|
def changeset(fingerprint_ban, attrs) do
|
||||||
fingerprint
|
fingerprint_ban
|
||||||
|> cast(attrs, [])
|
|> cast(attrs, [])
|
||||||
|> validate_required([])
|
|> populate_until()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def save_changeset(fingerprint_ban, attrs) do
|
||||||
|
fingerprint_ban
|
||||||
|
|> cast(attrs, [:reason, :note, :enabled, :fingerprint, :until])
|
||||||
|
|> populate_valid_until()
|
||||||
|
|> put_ban_id()
|
||||||
|
|> validate_required([:reason, :enabled, :fingerprint, :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, "F#{ban_id}")
|
||||||
|
end
|
||||||
|
defp put_ban_id(changeset), do: changeset
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.FingerprintBanController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias Philomena.Bans.Fingerprint, as: FingerprintBan
|
||||||
|
alias Philomena.Bans
|
||||||
|
alias Philomena.Repo
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
plug :verify_authorized
|
||||||
|
plug :load_resource, model: FingerprintBan, only: [:edit, :update, :delete]
|
||||||
|
|
||||||
|
def index(conn, %{"q" => q}) when is_binary(q) do
|
||||||
|
FingerprintBan
|
||||||
|
|> where([fb],
|
||||||
|
ilike(fb.fingerprint, ^"%#{q}%")
|
||||||
|
or fb.generated_ban_id == ^q
|
||||||
|
or fragment("to_tsvector(?) @@ plainto_tsquery(?)", fb.reason, ^q)
|
||||||
|
or fragment("to_tsvector(?) @@ plainto_tsquery(?)", fb.note, ^q)
|
||||||
|
)
|
||||||
|
|> load_bans(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index(conn, %{"fingerprint" => fingerprint}) when is_binary(fingerprint) do
|
||||||
|
FingerprintBan
|
||||||
|
|> where(fingerprint: ^fingerprint)
|
||||||
|
|> load_bans(conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
load_bans(FingerprintBan, conn)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(conn, %{"fingerprint" => fingerprint}) do
|
||||||
|
changeset = Bans.change_fingerprint(%FingerprintBan{fingerprint: fingerprint})
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(conn, _params) do
|
||||||
|
changeset = Bans.change_fingerprint(%FingerprintBan{})
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(conn, %{"fingerprint" => fingerprint_ban_params}) do
|
||||||
|
case Bans.create_fingerprint(conn.assigns.current_user, fingerprint_ban_params) do
|
||||||
|
{:ok, _fingerprint_ban} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Fingerprint was successfully banned.")
|
||||||
|
|> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index))
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit(conn, _params) do
|
||||||
|
changeset = Bans.change_fingerprint(conn.assigns.fingerprint)
|
||||||
|
render(conn, "edit.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(conn, %{"fingerprint" => fingerprint_ban_params}) do
|
||||||
|
case Bans.update_fingerprint(conn.assigns.fingerprint, fingerprint_ban_params) do
|
||||||
|
{:ok, _fingerprint_ban} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Fingerprint ban successfully updated.")
|
||||||
|
|> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index))
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
render(conn, "edit.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(conn, _params) do
|
||||||
|
{:ok, _fingerprint_ban} = Bans.delete_fingerprint(conn.assigns.fingerprint)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Fingerprint ban successfully deleted.")
|
||||||
|
|> redirect(to: Routes.admin_fingerprint_ban_path(conn, :index))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_bans(queryable, conn) do
|
||||||
|
fingerprint_bans =
|
||||||
|
queryable
|
||||||
|
|> order_by(desc: :created_at)
|
||||||
|
|> preload(:banning_user)
|
||||||
|
|> Repo.paginate(conn.assigns.scrivener)
|
||||||
|
|
||||||
|
render(conn, "index.html", layout_class: "layout--wide", fingerprint_bans: fingerprint_bans)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp verify_authorized(conn, _opts) do
|
||||||
|
case Canada.Can.can?(conn.assigns.current_user, :index, FingerprintBan) do
|
||||||
|
true -> conn
|
||||||
|
false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -46,7 +46,7 @@ defmodule PhilomenaWeb.Admin.SubnetBanController do
|
||||||
case Bans.create_subnet(conn.assigns.current_user, subnet_ban_params) do
|
case Bans.create_subnet(conn.assigns.current_user, subnet_ban_params) do
|
||||||
{:ok, _subnet_ban} ->
|
{:ok, _subnet_ban} ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "User was successfully banned.")
|
|> put_flash(:info, "Subnet was successfully banned.")
|
||||||
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
||||||
|
|
||||||
{:error, changeset} ->
|
{:error, changeset} ->
|
||||||
|
|
|
@ -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, :fingerprint, "Fingerprint:"
|
||||||
|
= text_input f, :fingerprint, class: "input", placeholder: "Fingerprint", 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.FingerprintBanView, "_form.html", changeset: @changeset, action: Routes.admin_fingerprint_ban_path(@conn, :update, @fingerprint), conn: @conn
|
||||||
|
|
||||||
|
br
|
||||||
|
= link "Back", to: Routes.admin_fingerprint_ban_path(@conn, :index)
|
|
@ -0,0 +1,52 @@
|
||||||
|
h1 Fingerprint Bans
|
||||||
|
|
||||||
|
- route = fn p -> Routes.admin_fingerprint_ban_path(@conn, :index, p) end
|
||||||
|
- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @fingerprint_bans, route: route
|
||||||
|
|
||||||
|
.block
|
||||||
|
.block__header
|
||||||
|
= pagination
|
||||||
|
|
||||||
|
.block__content
|
||||||
|
table.table
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Fingerprint
|
||||||
|
th Created
|
||||||
|
th Expires
|
||||||
|
th Reason/Note
|
||||||
|
th Ban ID
|
||||||
|
th Options
|
||||||
|
|
||||||
|
tbody
|
||||||
|
= for ban <- @fingerprint_bans do
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
= link ban.fingerprint, to: Routes.fingerprint_profile_path(@conn, :show, ban.fingerprint)
|
||||||
|
|
||||||
|
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_fingerprint_ban_path(@conn, :edit, ban)
|
||||||
|
' •
|
||||||
|
=> link "Destroy", to: Routes.admin_fingerprint_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 Fingerprint Ban
|
||||||
|
= render PhilomenaWeb.Admin.FingerprintBanView, "_form.html", changeset: @changeset, action: Routes.admin_fingerprint_ban_path(@conn, :create), conn: @conn
|
||||||
|
|
||||||
|
br
|
||||||
|
= link "Back", to: Routes.admin_fingerprint_ban_path(@conn, :index)
|
14
lib/philomena_web/views/admin/fingerprint_ban_view.ex
Normal file
14
lib/philomena_web/views/admin/fingerprint_ban_view.ex
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.FingerprintBanView 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
|
Loading…
Reference in a new issue