aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/plugs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/plugs')
-rw-r--r--lib/pleroma/plugs/authentication_plug.ex16
-rw-r--r--lib/pleroma/plugs/http_signature.ex49
-rw-r--r--lib/pleroma/plugs/mapped_signature_to_identity_plug.ex70
-rw-r--r--lib/pleroma/plugs/rate_limiter.ex67
4 files changed, 157 insertions, 45 deletions
diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex
index da4ed4226..567674a0b 100644
--- a/lib/pleroma/plugs/authentication_plug.ex
+++ b/lib/pleroma/plugs/authentication_plug.ex
@@ -6,9 +6,21 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
alias Comeonin.Pbkdf2
import Plug.Conn
alias Pleroma.User
+ require Logger
- def init(options) do
- options
+ def init(options), do: options
+
+ def checkpw(password, "$6" <> _ = password_hash) do
+ :crypt.crypt(password, password_hash) == password_hash
+ end
+
+ def checkpw(password, "$pbkdf2" <> _ = password_hash) do
+ Pbkdf2.checkpw(password, password_hash)
+ end
+
+ def checkpw(_password, _password_hash) do
+ Logger.error("Password hash not recognized")
+ false
end
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
diff --git a/lib/pleroma/plugs/http_signature.ex b/lib/pleroma/plugs/http_signature.ex
index e2874c469..d87fa52fa 100644
--- a/lib/pleroma/plugs/http_signature.ex
+++ b/lib/pleroma/plugs/http_signature.ex
@@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
- alias Pleroma.Web.ActivityPub.Utils
import Plug.Conn
require Logger
@@ -16,38 +15,30 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
end
def call(conn, _opts) do
- user = Utils.get_ap_id(conn.params["actor"])
- Logger.debug("Checking sig for #{user}")
[signature | _] = get_req_header(conn, "signature")
- cond do
- signature && String.contains?(signature, user) ->
- # set (request-target) header to the appropriate value
- # we also replace the digest header with the one we computed
- conn =
- conn
- |> put_req_header(
- "(request-target)",
- String.downcase("#{conn.method}") <> " #{conn.request_path}"
- )
-
- conn =
- if conn.assigns[:digest] do
- conn
- |> put_req_header("digest", conn.assigns[:digest])
- else
- conn
- end
-
- assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
+ if signature do
+ # set (request-target) header to the appropriate value
+ # we also replace the digest header with the one we computed
+ conn =
+ conn
+ |> put_req_header(
+ "(request-target)",
+ String.downcase("#{conn.method}") <> " #{conn.request_path}"
+ )
- signature ->
- Logger.debug("Signature not from actor")
- assign(conn, :valid_signature, false)
+ conn =
+ if conn.assigns[:digest] do
+ conn
+ |> put_req_header("digest", conn.assigns[:digest])
+ else
+ conn
+ end
- true ->
- Logger.debug("No signature header!")
- conn
+ assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
+ else
+ Logger.debug("No signature header!")
+ conn
end
end
end
diff --git a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex
new file mode 100644
index 000000000..ce8494b9d
--- /dev/null
+++ b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex
@@ -0,0 +1,70 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
+ alias Pleroma.Signature
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Utils
+
+ import Plug.Conn
+ require Logger
+
+ def init(options), do: options
+
+ defp key_id_from_conn(conn) do
+ with %{"keyId" => key_id} <- HTTPSignatures.signature_for_conn(conn) do
+ Signature.key_id_to_actor_id(key_id)
+ else
+ _ ->
+ nil
+ end
+ end
+
+ defp user_from_key_id(conn) do
+ with key_actor_id when is_binary(key_actor_id) <- key_id_from_conn(conn),
+ {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(key_actor_id) do
+ user
+ else
+ _ ->
+ nil
+ end
+ end
+
+ def call(%{assigns: %{user: _}} = conn, _opts), do: conn
+
+ # if this has payload make sure it is signed by the same actor that made it
+ def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = conn, _opts) do
+ with actor_id <- Utils.get_ap_id(actor),
+ {:user, %User{} = user} <- {:user, user_from_key_id(conn)},
+ {:user_match, true} <- {:user_match, user.ap_id == actor_id} do
+ assign(conn, :user, user)
+ else
+ {:user_match, false} ->
+ Logger.debug("Failed to map identity from signature (payload actor mismatch)")
+ Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}")
+ assign(conn, :valid_signature, false)
+
+ # remove me once testsuite uses mapped capabilities instead of what we do now
+ {:user, nil} ->
+ Logger.debug("Failed to map identity from signature (lookup failure)")
+ Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}")
+ conn
+ end
+ end
+
+ # no payload, probably a signed fetch
+ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
+ with %User{} = user <- user_from_key_id(conn) do
+ assign(conn, :user, user)
+ else
+ _ ->
+ Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
+ Logger.debug("key_id=#{key_id_from_conn(conn)}")
+ assign(conn, :valid_signature, false)
+ end
+ end
+
+ # no signature at all
+ def call(conn, _opts), do: conn
+end
diff --git a/lib/pleroma/plugs/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter.ex
index c5e0957e8..31388f574 100644
--- a/lib/pleroma/plugs/rate_limiter.ex
+++ b/lib/pleroma/plugs/rate_limiter.ex
@@ -31,12 +31,28 @@ defmodule Pleroma.Plugs.RateLimiter do
## Usage
+ AllowedSyntax:
+
+ plug(Pleroma.Plugs.RateLimiter, :limiter_name)
+ plug(Pleroma.Plugs.RateLimiter, {:limiter_name, options})
+
+ Allowed options:
+
+ * `bucket_name` overrides bucket name (e.g. to have a separate limit for a set of actions)
+ * `params` appends values of specified request params (e.g. ["id"]) to bucket name
+
Inside a controller:
plug(Pleroma.Plugs.RateLimiter, :one when action == :one)
plug(Pleroma.Plugs.RateLimiter, :two when action in [:two, :three])
- or inside a router pipiline:
+ plug(
+ Pleroma.Plugs.RateLimiter,
+ {:status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]}
+ when action in ~w(fav_status unfav_status)a
+ )
+
+ or inside a router pipeline:
pipeline :api do
...
@@ -49,33 +65,56 @@ defmodule Pleroma.Plugs.RateLimiter do
alias Pleroma.User
- def init(limiter_name) do
+ def init(limiter_name) when is_atom(limiter_name) do
+ init({limiter_name, []})
+ end
+
+ def init({limiter_name, opts}) do
case Pleroma.Config.get([:rate_limit, limiter_name]) do
nil -> nil
- config -> {limiter_name, config}
+ config -> {limiter_name, config, opts}
end
end
- # do not limit if there is no limiter configuration
+ # Do not limit if there is no limiter configuration
def call(conn, nil), do: conn
- def call(conn, opts) do
- case check_rate(conn, opts) do
- {:ok, _count} -> conn
- {:error, _count} -> render_throttled_error(conn)
+ def call(conn, settings) do
+ case check_rate(conn, settings) do
+ {:ok, _count} ->
+ conn
+
+ {:error, _count} ->
+ render_throttled_error(conn)
+ end
+ end
+
+ defp bucket_name(conn, limiter_name, opts) do
+ bucket_name = opts[:bucket_name] || limiter_name
+
+ if params_names = opts[:params] do
+ params_values = for p <- Enum.sort(params_names), do: conn.params[p]
+ Enum.join([bucket_name] ++ params_values, ":")
+ else
+ bucket_name
end
end
- defp check_rate(%{assigns: %{user: %User{id: user_id}}}, {limiter_name, [_, {scale, limit}]}) do
- ExRated.check_rate("#{limiter_name}:#{user_id}", scale, limit)
+ defp check_rate(
+ %{assigns: %{user: %User{id: user_id}}} = conn,
+ {limiter_name, [_, {scale, limit}], opts}
+ ) do
+ bucket_name = bucket_name(conn, limiter_name, opts)
+ ExRated.check_rate("#{bucket_name}:#{user_id}", scale, limit)
end
- defp check_rate(conn, {limiter_name, [{scale, limit} | _]}) do
- ExRated.check_rate("#{limiter_name}:#{ip(conn)}", scale, limit)
+ defp check_rate(conn, {limiter_name, [{scale, limit} | _], opts}) do
+ bucket_name = bucket_name(conn, limiter_name, opts)
+ ExRated.check_rate("#{bucket_name}:#{ip(conn)}", scale, limit)
end
- defp check_rate(conn, {limiter_name, {scale, limit}}) do
- check_rate(conn, {limiter_name, [{scale, limit}]})
+ defp check_rate(conn, {limiter_name, {scale, limit}, opts}) do
+ check_rate(conn, {limiter_name, [{scale, limit}, {scale, limit}], opts})
end
def ip(%{remote_ip: remote_ip}) do