Optionally store user IP addresses, #1708
This commit is contained in:
parent
5e128a6be3
commit
4c76e3e3b7
@ -686,6 +686,8 @@ config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
|||||||
"192.168.0.0/16"
|
"192.168.0.0/16"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Plugs.StoreUserIpPlug, enabled: false
|
||||||
|
|
||||||
config :pleroma, :static_fe, enabled: false
|
config :pleroma, :static_fe, enabled: false
|
||||||
|
|
||||||
# Example of frontend configuration
|
# Example of frontend configuration
|
||||||
|
@ -2844,7 +2844,7 @@ config :pleroma, :config_description, [
|
|||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enable/disable the plug. Default: disabled."
|
description: "Enable/disable the plug. Default: enabled."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :headers,
|
key: :headers,
|
||||||
@ -2870,6 +2870,21 @@ config :pleroma, :config_description, [
|
|||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
|
key: Pleroma.Web.Plugs.StoreUserIpPlug,
|
||||||
|
type: :group,
|
||||||
|
description: """
|
||||||
|
Stores the user's last known IP address in the database if enabled. IP addresses are shown in AdminAPI.
|
||||||
|
""",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enabled,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Enable/disable the plug. Default: disabled."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
key: :web_cache_ttl,
|
key: :web_cache_ttl,
|
||||||
label: "Web cache TTL",
|
label: "Web cache TTL",
|
||||||
type: :group,
|
type: :group,
|
||||||
|
22
lib/pleroma/ecto_type/ip_address.ex
Normal file
22
lib/pleroma/ecto_type/ip_address.ex
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.EctoType.IpAddress do
|
||||||
|
alias Postgrex.INET
|
||||||
|
@behaviour Ecto.Type
|
||||||
|
|
||||||
|
def type, do: :inet
|
||||||
|
|
||||||
|
def cast(%INET{address: ip, netmask: nil}), do: {:ok, ip}
|
||||||
|
def cast(ip) when is_tuple(ip), do: {:ok, ip}
|
||||||
|
|
||||||
|
def load(%INET{address: ip, netmask: nil}), do: {:ok, ip}
|
||||||
|
|
||||||
|
def dump(ip) when is_tuple(ip), do: {:ok, %INET{address: ip, netmask: nil}}
|
||||||
|
def dump(_), do: :error
|
||||||
|
|
||||||
|
def equal?(a, b), do: a == b
|
||||||
|
|
||||||
|
def embed_as(_), do: :self
|
||||||
|
end
|
@ -146,6 +146,7 @@ defmodule Pleroma.User do
|
|||||||
field(:inbox, :string)
|
field(:inbox, :string)
|
||||||
field(:shared_inbox, :string)
|
field(:shared_inbox, :string)
|
||||||
field(:accepts_chat_messages, :boolean, default: nil)
|
field(:accepts_chat_messages, :boolean, default: nil)
|
||||||
|
field(:last_known_ip, Pleroma.EctoType.IpAddress)
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
@ -2457,4 +2458,12 @@ defmodule Pleroma.User do
|
|||||||
def get_host(%User{ap_id: ap_id} = _user) do
|
def get_host(%User{ap_id: ap_id} = _user) do
|
||||||
URI.parse(ap_id).host
|
URI.parse(ap_id).host
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_last_known_ip(%User{last_known_ip: ip} = user, ip), do: {:ok, user}
|
||||||
|
|
||||||
|
def update_last_known_ip(%User{} = user, ip) when is_tuple(ip) do
|
||||||
|
user
|
||||||
|
|> change(last_known_ip: ip)
|
||||||
|
|> update_and_set_cache()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
39
lib/pleroma/web/plugs/store_user_ip_plug.ex
Normal file
39
lib/pleroma/web/plugs/store_user_ip_plug.ex
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.StoreUserIpPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Stores the user's last known IP address in the database if enabled.
|
||||||
|
User IP addresses are shown in AdminAPI.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.User
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
def init(_), do: nil
|
||||||
|
|
||||||
|
# IP address hasn't changed, so skip
|
||||||
|
def call(
|
||||||
|
%{remote_ip: ip, assigns: %{remote_ip_found: true, user: %User{last_known_ip: ip}}} =
|
||||||
|
conn,
|
||||||
|
_
|
||||||
|
),
|
||||||
|
do: conn
|
||||||
|
|
||||||
|
# Store user IP if enabled
|
||||||
|
def call(%{remote_ip: ip, assigns: %{remote_ip_found: true, user: %User{} = user}} = conn, _) do
|
||||||
|
with true <- Config.get([__MODULE__, :enabled]),
|
||||||
|
{:ok, %User{} = user} <- User.update_last_known_ip(user, ip) do
|
||||||
|
assign(conn, :user, user)
|
||||||
|
else
|
||||||
|
_ -> conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fail silently
|
||||||
|
def call(conn, _), do: conn
|
||||||
|
end
|
@ -56,6 +56,7 @@ defmodule Pleroma.Web.Router do
|
|||||||
plug(Pleroma.Web.Plugs.UserEnabledPlug)
|
plug(Pleroma.Web.Plugs.UserEnabledPlug)
|
||||||
plug(Pleroma.Web.Plugs.SetUserSessionIdPlug)
|
plug(Pleroma.Web.Plugs.SetUserSessionIdPlug)
|
||||||
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
||||||
|
plug(Pleroma.Web.Plugs.StoreUserIpPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :base_api do
|
pipeline :base_api do
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
defmodule Pleroma.Repo.Migrations.AddLastKnownIpToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:last_known_ip, :inet)
|
||||||
|
end
|
||||||
|
|
||||||
|
create(index(:users, [:last_known_ip]))
|
||||||
|
end
|
||||||
|
end
|
@ -2271,4 +2271,11 @@ defmodule Pleroma.UserTest do
|
|||||||
user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
|
user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
|
||||||
assert User.get_host(user) == "lain.com"
|
assert User.get_host(user) == "lain.com"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "update_last_known_ip/2" do
|
||||||
|
%User{id: user_id} = user = insert(:user, last_known_ip: {1, 2, 3, 4})
|
||||||
|
|
||||||
|
assert {:ok, %User{id: ^user_id, last_known_ip: {5, 4, 3, 2}}} =
|
||||||
|
User.update_last_known_ip(user, {5, 4, 3, 2})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
59
test/pleroma/web/plugs/store_user_ip_plug_test.exs
Normal file
59
test/pleroma/web/plugs/store_user_ip_plug_test.exs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plugs.StoreUserIpPlugTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
use Plug.Test
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.Plugs.RemoteIp
|
||||||
|
alias Pleroma.Web.Plugs.StoreUserIpPlug
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
setup do: clear_config(StoreUserIpPlug, enabled: true)
|
||||||
|
|
||||||
|
setup do:
|
||||||
|
clear_config(RemoteIp,
|
||||||
|
enabled: true,
|
||||||
|
headers: ["x-forwarded-for"],
|
||||||
|
proxies: [],
|
||||||
|
reserved: [
|
||||||
|
"127.0.0.0/8",
|
||||||
|
"::1/128",
|
||||||
|
"fc00::/7",
|
||||||
|
"10.0.0.0/8",
|
||||||
|
"172.16.0.0/12",
|
||||||
|
"192.168.0.0/16"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
test "stores the user's IP address", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("x-forwarded-for", "1.2.3.4")
|
||||||
|
|> RemoteIp.call(nil)
|
||||||
|
|> StoreUserIpPlug.call(nil)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
assert user.last_known_ip == {1, 2, 3, 4}
|
||||||
|
assert %Plug.Conn{assigns: %{user: %User{last_known_ip: {1, 2, 3, 4}} = ^user}} = conn
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does nothing when disabled", %{conn: conn} do
|
||||||
|
clear_config(StoreUserIpPlug, enabled: false)
|
||||||
|
user = insert(:user, last_known_ip: {1, 2, 3, 4})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("x-forwarded-for", "5.4.3.2")
|
||||||
|
|> RemoteIp.call(nil)
|
||||||
|
|> StoreUserIpPlug.call(nil)
|
||||||
|
|
||||||
|
assert user == User.get_by_id(user.id)
|
||||||
|
assert %Plug.Conn{assigns: %{user: %User{last_known_ip: {1, 2, 3, 4}} = ^user}} = conn
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user