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{}}
|
||||
|
||||
"""
|
||||
def create_fingerprint(attrs \\ %{}) do
|
||||
%Fingerprint{}
|
||||
|> Fingerprint.changeset(attrs)
|
||||
def create_fingerprint(creator, attrs \\ %{}) do
|
||||
%Fingerprint{banning_user_id: creator.id}
|
||||
|> Fingerprint.save_changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
|
@ -69,7 +69,7 @@ defmodule Philomena.Bans do
|
|||
"""
|
||||
def update_fingerprint(%Fingerprint{} = fingerprint, attrs) do
|
||||
fingerprint
|
||||
|> Fingerprint.changeset(attrs)
|
||||
|> Fingerprint.save_changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ defmodule Philomena.Bans.Fingerprint do
|
|||
import Ecto.Changeset
|
||||
|
||||
alias Philomena.Users.User
|
||||
alias RelativeDate.Parser
|
||||
|
||||
schema "fingerprint_bans" do
|
||||
belongs_to :banning_user, User
|
||||
|
@ -14,13 +15,47 @@ defmodule Philomena.Bans.Fingerprint do
|
|||
field :fingerprint, :string
|
||||
field :generated_ban_id, :string
|
||||
|
||||
field :until, :string, virtual: true
|
||||
|
||||
timestamps(inserted_at: :created_at)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(fingerprint, attrs) do
|
||||
fingerprint
|
||||
def changeset(fingerprint_ban, attrs) do
|
||||
fingerprint_ban
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
|> populate_until()
|
||||
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
|
||||
|
|
|
@ -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
|
||||
{:ok, _subnet_ban} ->
|
||||
conn
|
||||
|> put_flash(:info, "User was successfully banned.")
|
||||
|> put_flash(:info, "Subnet was successfully banned.")
|
||||
|> redirect(to: Routes.admin_subnet_ban_path(conn, :index))
|
||||
|
||||
{: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