diff --git a/lib/philomena/users.ex b/lib/philomena/users.ex index 39abe930..393f0a12 100644 --- a/lib/philomena/users.ex +++ b/lib/philomena/users.ex @@ -177,7 +177,7 @@ defmodule Philomena.Users do defp setup_roles(nil), do: nil defp setup_roles(user) do - role_map = Map.new(user.roles, &{&1.name, &1.resource_type || true}) + role_map = Map.new(user.roles, &{&1.resource_type || &1.name, &1.name}) %{user | role_map: role_map} end diff --git a/lib/philomena/users/ability.ex b/lib/philomena/users/ability.ex index b65f7a7a..63380d35 100644 --- a/lib/philomena/users/ability.ex +++ b/lib/philomena/users/ability.ex @@ -3,6 +3,7 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do alias Philomena.Comments.Comment alias Philomena.Commissions.Commission alias Philomena.Conversations.Conversation + alias Philomena.DuplicateReports.DuplicateReport alias Philomena.Images.Image alias Philomena.Forums.Forum alias Philomena.Topics.Topic @@ -11,6 +12,7 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do alias Philomena.Galleries.Gallery alias Philomena.DnpEntries.DnpEntry alias Philomena.UserLinks.UserLink + alias Philomena.Tags.Tag # Admins can do anything def can?(%User{role: "admin"}, _action, _model), do: true @@ -36,12 +38,43 @@ defimpl Canada.Can, for: [Atom, Philomena.Users.User] do # View conversations def can?(%User{role: "moderator"}, :show, %Conversation{}), do: true + # View IP addresses and fingerprints + def can?(%User{role: "moderator"}, :show, :ip_address), do: true + # # Assistants can... # - # View images - def can?(%User{role: "assistant"}, :show, %Image{}), do: true + + # Image assistant actions + def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :show, %Image{}), do: true + def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :hide, %Image{}), do: true + def can?(%User{role: "assistant", role_map: %{"Image" => "moderator"}}, :edit, %Image{}), do: true + + # Dupe assistant actions + def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :edit, %DuplicateReport{}), do: true + def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :show, %Image{}), do: true + def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :edit, %Image{}), do: true + def can?(%User{role: "assistant", role_map: %{"DuplicateReport" => "moderator"}}, :hide, %Comment{}), do: true + + # Comment assistant actions + def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :show, %Comment{}), do: true + def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :edit, %Comment{}), do: true + def can?(%User{role: "assistant", role_map: %{"Comment" => "moderator"}}, :hide, %Comment{}), do: true + + # Topic assistant actions + def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Topic{}), do: true + def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Topic{}), do: true + def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :show, %Post{}), do: true + def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :edit, %Post{}), do: true + def can?(%User{role: "assistant", role_map: %{"Topic" => "moderator"}}, :hide, %Post{}), do: true + + # Tag assistant actions + def can?(%User{role: "assistant", role_map: %{"Tag" => "moderator"}}, :edit, %Tag{}), do: true + + # User link assistant actions + def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :show, %UserLink{}), do: true + def can?(%User{role: "assistant", role_map: %{"UserLink" => "moderator"}}, :edit, %UserLink{}), do: true # View forums def can?(%User{role: "assistant"}, :show, %Forum{access_level: level}) diff --git a/lib/philomena_web/controllers/fingerprint_profile_controller.ex b/lib/philomena_web/controllers/fingerprint_profile_controller.ex new file mode 100644 index 00000000..168bcf9d --- /dev/null +++ b/lib/philomena_web/controllers/fingerprint_profile_controller.ex @@ -0,0 +1,33 @@ +defmodule PhilomenaWeb.FingerprintProfileController do + use PhilomenaWeb, :controller + + alias Philomena.UserFingerprints.UserFingerprint + alias Philomena.Bans.Fingerprint + alias Philomena.Repo + import Ecto.Query + + plug :authorize_ip + + def show(conn, %{"id" => fingerprint}) do + user_fps = + UserFingerprint + |> where(fingerprint: ^fingerprint) + |> order_by(desc: :updated_at) + |> preload(:user) + |> Repo.all() + + fp_bans = + Fingerprint + |> where(fingerprint: ^fingerprint) + |> Repo.all() + + render(conn, "show.html", fingerprint: fingerprint, user_fps: user_fps, fp_bans: fp_bans) + end + + defp authorize_ip(conn, _opts) do + case Canada.Can.can?(conn.assigns.current_user, :show, :ip_address) do + false -> PhilomenaWeb.NotAuthorizedPlug.call(conn) + true -> conn + end + end +end \ No newline at end of file diff --git a/lib/philomena_web/controllers/ip_profile_controller.ex b/lib/philomena_web/controllers/ip_profile_controller.ex new file mode 100644 index 00000000..d828c346 --- /dev/null +++ b/lib/philomena_web/controllers/ip_profile_controller.ex @@ -0,0 +1,35 @@ +defmodule PhilomenaWeb.IpProfileController do + use PhilomenaWeb, :controller + + alias Philomena.UserIps.UserIp + alias Philomena.Bans.Subnet + alias Philomena.Repo + import Ecto.Query + + plug :authorize_ip + + def show(conn, %{"id" => ip}) do + {:ok, ip} = EctoNetwork.INET.cast(ip) + + user_ips = + UserIp + |> where(ip: ^ip) + |> order_by(desc: :updated_at) + |> preload(:user) + |> Repo.all() + + subnet_bans = + Subnet + |> where([s], fragment("? >>= ?", s.specification, ^ip)) + |> Repo.all() + + render(conn, "show.html", ip: ip, user_ips: user_ips, subnet_bans: subnet_bans) + end + + defp authorize_ip(conn, _opts) do + case Canada.Can.can?(conn.assigns.current_user, :show, :ip_address) do + false -> PhilomenaWeb.NotAuthorizedPlug.call(conn) + true -> conn + end + end +end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 2990d9e9..322ca263 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -151,6 +151,9 @@ defmodule PhilomenaWeb.Router do resources "/read", Channel.ReadController, only: [:create], singleton: true resources "/subscription", Channel.SubscriptionController, only: [:create, :delete], singleton: true end + + resources "/ip_profiles", IpProfileController, only: [:show] + resources "/fingerprint_profiles", FingerprintProfileController, only: [:show] end scope "/", PhilomenaWeb do diff --git a/lib/philomena_web/templates/comment/_comment.html.slime b/lib/philomena_web/templates/comment/_comment.html.slime index b34eb702..38ae6ee8 100644 --- a/lib/philomena_web/templates/comment/_comment.html.slime +++ b/lib/philomena_web/templates/comment/_comment.html.slime @@ -24,9 +24,9 @@ article.block.communication id="comment_#{@comment.id}" =<> "Delete" = if can?(@conn, :manage, @comment) do .communication__info - =<> link_to_ip(@comment.ip) + =<> link_to_ip(@conn, @comment.ip) .communication__info - =<> link_to_fingerprint(@comment.fingerprint) + =<> link_to_fingerprint(@conn, @comment.fingerprint) /- if can?(:hide, Comment) / .js-staff-action / - if !comment.hidden_from_users && !comment.destroyed_content diff --git a/lib/philomena_web/templates/fingerprint_profile/show.html.slime b/lib/philomena_web/templates/fingerprint_profile/show.html.slime new file mode 100644 index 00000000..00b24778 --- /dev/null +++ b/lib/philomena_web/templates/fingerprint_profile/show.html.slime @@ -0,0 +1,39 @@ +h1 + = @fingerprint + ' 's fingerprint profile + +ul + li = link "View images this fingerprint has uploaded", to: Routes.search_path(@conn, :index, q: "fingerprint:#{@fingerprint}") + li = link "View comments this fingerprint has posted", to: Routes.comment_path(@conn, :index, cq: "fingerprint:#{@fingerprint}") + li = link "View posts this fingerprint has made", to: Routes.post_path(@conn, :index, pq: "fingerprint:#{@fingerprint}") + +/= render partial: "bans/ban_list", locals: { bans: @bans } + +h2 Administration Options +/ul + li = link "View tag changes", "/fingerprint_profiles/#{@fingerprint}/tag_changes" + li = link "View source URL history", "/fingerprint_profiles/#{@fingerprint}/source_changes" + li = link "View reports this fingerprint has made", admin_reports_path(rq: "ip:#{@fingerprint}") + li = link "View fingerprint ban history", admin_subnet_bans_path(q: @fingerprint) + li = link "Ban this sucker", new_admin_subnet_ban_path(fingerprint: @fingerprint) + +h4 Observed users +table.table + thead + tr + th Username + th Account Used + th Last Seen + th Created At + tbody + = for ufp <- @user_fps do + tr + td + = link ufp.user.name, to: Routes.profile_path(@conn, :show, ufp.user) + td + => ufp.uses + ' times + td + => pretty_time ufp.updated_at + td + => pretty_time ufp.user.created_at diff --git a/lib/philomena_web/templates/image/_image_meta.html.slime b/lib/philomena_web/templates/image/_image_meta.html.slime index 1df61a8b..200c763a 100644 --- a/lib/philomena_web/templates/image/_image_meta.html.slime +++ b/lib/philomena_web/templates/image/_image_meta.html.slime @@ -55,8 +55,8 @@ ' by => render PhilomenaWeb.UserAttributionView, "_anon_user.html", object: @image, awards: true, conn: @conn = if can?(@conn, :manage, @image) do - =<> link_to_ip(@image.ip) - =<> link_to_fingerprint(@image.fingerprint) + => link_to_ip(@conn, @image.ip) + => link_to_fingerprint(@conn, @image.fingerprint) a href="#" i.fa.fa-edit span.image-size diff --git a/lib/philomena_web/templates/ip_profile/show.html.slime b/lib/philomena_web/templates/ip_profile/show.html.slime new file mode 100644 index 00000000..b0aca083 --- /dev/null +++ b/lib/philomena_web/templates/ip_profile/show.html.slime @@ -0,0 +1,39 @@ +h1 + = @ip + ' 's IP profile + +ul + li = link "View images this IP has uploaded", to: Routes.search_path(@conn, :index, q: "ip:#{@ip}") + li = link "View comments this IP has posted", to: Routes.comment_path(@conn, :index, cq: "ip:#{@ip}") + li = link "View posts this IP has made", to: Routes.post_path(@conn, :index, pq: "ip:#{@ip}") + +/= render partial: "bans/ban_list", locals: { bans: @bans } + +h2 Administration Options +/ul + li = link "View tag changes", "/ip_profiles/#{@ip}/tag_changes" + li = link "View source URL history", "/ip_profiles/#{@ip}/source_changes" + li = link "View reports this IP has made", admin_reports_path(rq: "ip:#{@ip}") + li = link "View IP ban history", admin_subnet_bans_path(q: @ip) + li = link "Ban this sucker", new_admin_subnet_ban_path(ip: @ip) + +h4 Observed users +table.table + thead + tr + th Username + th Account Used + th Last Seen + th Created At + tbody + = for uip <- @user_ips do + tr + td + = link uip.user.name, to: Routes.profile_path(@conn, :show, uip.user) + td + => uip.uses + ' times + td + => pretty_time uip.updated_at + td + => pretty_time uip.user.created_at diff --git a/lib/philomena_web/templates/post/_post.html.slime b/lib/philomena_web/templates/post/_post.html.slime index 00db8bc6..f626f117 100644 --- a/lib/philomena_web/templates/post/_post.html.slime +++ b/lib/philomena_web/templates/post/_post.html.slime @@ -23,9 +23,9 @@ article.block.communication id="post_#{@post.id}" =<> "Delete" = if can?(@conn, :manage, @post) do .communication__info - =<> link_to_ip(@post.ip) + =<> link_to_ip(@conn, @post.ip) .communication__info - =<> link_to_fingerprint(@post.fingerprint) + =<> link_to_fingerprint(@conn, @post.fingerprint) /- if can?(:hide, Post) / .js-staff-action / - if !post.hidden_from_users && !post.destroyed_content diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex index ca344c0e..55ceaf9c 100644 --- a/lib/philomena_web/views/app_view.ex +++ b/lib/philomena_web/views/app_view.ex @@ -1,4 +1,5 @@ defmodule PhilomenaWeb.AppView do + alias PhilomenaWeb.Router.Helpers, as: Routes use Phoenix.HTML @time_strings %{ @@ -117,14 +118,12 @@ defmodule PhilomenaWeb.AppView do defp text_or_na(nil), do: "N/A" defp text_or_na(text), do: text - # todo: make ip a real link - def link_to_ip(ip) do - link(text_or_na(ip), to: "#") + def link_to_ip(conn, ip) do + link(text_or_na(ip), to: Routes.ip_profile_path(conn, :show, to_string(ip))) end - # todo: make fp a real link - def link_to_fingerprint(fp) do - link(String.slice(text_or_na(fp), 0..6), to: "#") + def link_to_fingerprint(conn, fp) do + link(String.slice(text_or_na(fp), 0..6), to: Routes.fingerprint_profile_path(conn, :show, fp)) end def blank?(nil), do: true diff --git a/lib/philomena_web/views/fingerprint_profile_view.ex b/lib/philomena_web/views/fingerprint_profile_view.ex new file mode 100644 index 00000000..30082835 --- /dev/null +++ b/lib/philomena_web/views/fingerprint_profile_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.FingerprintProfileView do + use PhilomenaWeb, :view +end diff --git a/lib/philomena_web/views/ip_profile_view.ex b/lib/philomena_web/views/ip_profile_view.ex new file mode 100644 index 00000000..9aef6c29 --- /dev/null +++ b/lib/philomena_web/views/ip_profile_view.ex @@ -0,0 +1,3 @@ +defmodule PhilomenaWeb.IpProfileView do + use PhilomenaWeb, :view +end