Validate multilang map
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
023eae28d6
commit
a9b1589528
7 changed files with 209 additions and 10 deletions
|
@ -10,11 +10,11 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString do
|
|||
def type, do: :map
|
||||
|
||||
def cast(object) do
|
||||
with {status, %{} = data} when status in [:modified, :ok] <- MultiLanguage.validate_map(object) do
|
||||
with {status, %{} = data} when status in [:modified, :ok] <-
|
||||
MultiLanguage.validate_map(object) do
|
||||
{:ok, data}
|
||||
else
|
||||
{:modified, nil} -> {:ok, nil}
|
||||
|
||||
{:error, _} -> :error
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,6 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
import Ecto.Query
|
||||
import Pleroma.Web.ActivityPub.Utils
|
||||
import Pleroma.Web.ActivityPub.Visibility
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Webhook.Notify, only: [trigger_webhooks: 2]
|
||||
|
||||
require Logger
|
||||
|
@ -1591,12 +1592,27 @@ def fetch_activities_bounded(
|
|||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
defp validate_media_description_map(%{} = map) do
|
||||
with {:ok, %{}} <- Pleroma.MultiLanguage.validate_map(map) do
|
||||
:ok
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_media_description_map(nil), do: :ok
|
||||
defp validate_media_description_map(_), do: :error
|
||||
|
||||
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||
def upload(file, opts \\ []) do
|
||||
with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
|
||||
with {_, :ok} <- {:description_map, validate_media_description_map(opts[:description_map])},
|
||||
{:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
|
||||
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
|
||||
|
||||
Repo.insert(%Object{data: obj_data})
|
||||
else
|
||||
{:description_map, _} -> {:error, dgettext("errors", "description_map invalid")}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ def create(user, params) do
|
|||
|> status()
|
||||
|> summary()
|
||||
|> with_valid(&attachments/1)
|
||||
|> full_payload()
|
||||
|> with_valid(&full_payload/1)
|
||||
|> expires_at()
|
||||
|> poll()
|
||||
|> with_valid(&in_reply_to/1)
|
||||
|
@ -76,7 +76,7 @@ def create(user, params) do
|
|||
|> with_valid("e_post/1)
|
||||
|> with_valid(&visibility/1)
|
||||
|> with_valid("ing_visibility/1)
|
||||
|> content()
|
||||
|> with_valid(&content/1)
|
||||
|> with_valid(&to_and_cc/1)
|
||||
|> with_valid(&context/1)
|
||||
|> with_valid(&language/1)
|
||||
|
@ -153,16 +153,24 @@ defp put_params(draft, params) do
|
|||
%__MODULE__{draft | params: params}
|
||||
end
|
||||
|
||||
defp status(%{params: %{status_map: status_map}} = draft) do
|
||||
%__MODULE__{draft | status_map: status_map}
|
||||
defp status(%{params: %{status_map: %{} = status_map}} = draft) do
|
||||
with {:ok, %{}} <- MultiLanguage.validate_map(status_map) do
|
||||
%__MODULE__{draft | status_map: status_map}
|
||||
else
|
||||
_ -> add_error(draft, dgettext("errors", "status_map is not a valid multilang map"))
|
||||
end
|
||||
end
|
||||
|
||||
defp status(%{params: %{status: status}} = draft) do
|
||||
%__MODULE__{draft | status: String.trim(status)}
|
||||
end
|
||||
|
||||
defp summary(%{params: %{spoiler_text_map: spoiler_text_map}} = draft) do
|
||||
%__MODULE__{draft | summary_map: spoiler_text_map}
|
||||
defp summary(%{params: %{spoiler_text_map: %{} = spoiler_text_map}} = draft) do
|
||||
with {:ok, %{}} <- MultiLanguage.validate_map(spoiler_text_map) do
|
||||
%__MODULE__{draft | summary_map: spoiler_text_map}
|
||||
else
|
||||
_ -> add_error(draft, dgettext("errors", "spoiler_text_map is not a valid multilang map"))
|
||||
end
|
||||
end
|
||||
|
||||
defp summary(%{params: params} = draft) do
|
||||
|
|
|
@ -156,6 +156,7 @@ def make_poll_data(%{poll: %{options_map: options_map, expires_in: expires_in}}
|
|||
options = options |> Enum.uniq()
|
||||
|
||||
with :ok <- validate_poll_expiration(expires_in, limits),
|
||||
:ok <- validate_poll_options_map(options_map),
|
||||
:ok <- validate_poll_options_amount(options_map, limits),
|
||||
:ok <- validate_poll_options_length(options_map, limits) do
|
||||
{option_notes, emoji} =
|
||||
|
@ -211,6 +212,20 @@ def make_poll_data(_data) do
|
|||
{:ok, {%{}, %{}}}
|
||||
end
|
||||
|
||||
defp validate_poll_options_map(options) do
|
||||
if Enum.all?(options, fn opt ->
|
||||
with {:ok, %{}} <- MultiLanguage.validate_map(opt) do
|
||||
true
|
||||
else
|
||||
_ -> false
|
||||
end
|
||||
end) do
|
||||
:ok
|
||||
else
|
||||
{:error, dgettext("errors", "Poll option map not valid")}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_poll_options_amount(options, %{max_options: max_options}) do
|
||||
cond do
|
||||
Enum.count(options) < 2 ->
|
||||
|
|
|
@ -34,6 +34,11 @@ def create(
|
|||
attachment_data = Map.put(object.data, "id", object.id)
|
||||
|
||||
render(conn, "attachment.json", %{attachment: attachment_data})
|
||||
else
|
||||
{:error, e} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: e})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -57,6 +62,11 @@ def create2(
|
|||
conn
|
||||
|> put_status(202)
|
||||
|> render("attachment.json", %{attachment: attachment_data})
|
||||
else
|
||||
{:error, e} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{error: e})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -67,6 +67,26 @@ test "/api/v1/media, multilang", %{conn: conn, image: image} do
|
|||
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
|
||||
end
|
||||
|
||||
test "/api/v1/media, multilang, invalid description_map", %{conn: conn, image: image} do
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/media", %{
|
||||
"file" => image,
|
||||
"description_map" => %{"a" => "mew", "b_" => "lol"}
|
||||
})
|
||||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
|
||||
test "/api/v1/media, multilang, empty description_map", %{conn: conn, image: image} do
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v1/media", %{
|
||||
"file" => image,
|
||||
"description_map" => %{}
|
||||
})
|
||||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
|
||||
test "/api/v2/media", %{conn: conn, user: user, image: image} do
|
||||
desc = "Description of the image"
|
||||
|
||||
|
@ -111,6 +131,26 @@ test "/api/v2/media, multilang", %{conn: conn, image: image} do
|
|||
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
|
||||
end
|
||||
|
||||
test "/api/v2/media, multilang, invalid description_map", %{conn: conn, image: image} do
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v2/media", %{
|
||||
"file" => image,
|
||||
"description_map" => %{"a" => "mew", "b_" => "lol"}
|
||||
})
|
||||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
|
||||
test "/api/v2/media, multilang, empty description_map", %{conn: conn, image: image} do
|
||||
conn
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> post("/api/v2/media", %{
|
||||
"file" => image,
|
||||
"description_map" => %{}
|
||||
})
|
||||
|> json_response_and_validate_schema(422)
|
||||
end
|
||||
|
||||
test "/api/v2/media, upload_limit", %{conn: conn, user: user} do
|
||||
desc = "Description of the binary"
|
||||
|
||||
|
@ -133,7 +173,7 @@ test "/api/v2/media, upload_limit", %{conn: conn, user: user} do
|
|||
"file" => large_binary,
|
||||
"description" => desc
|
||||
})
|
||||
|> json_response_and_validate_schema(400)
|
||||
|> json_response_and_validate_schema(422)
|
||||
end) =~
|
||||
"[error] Elixir.Pleroma.Upload store (using Pleroma.Uploaders.Local) failed: :file_too_large"
|
||||
|
||||
|
|
|
@ -164,6 +164,78 @@ test "posting a multilang status", %{conn: conn} do
|
|||
assert Activity.get_by_id(id)
|
||||
end
|
||||
|
||||
test "posting a multilang status, invalid language code in status_map", %{conn: conn} do
|
||||
idempotency_key = "Pikachu rocks!"
|
||||
|
||||
conn_one =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put_req_header("idempotency-key", idempotency_key)
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status_map" => %{"a" => "mew mew", "b_" => "lol lol"},
|
||||
"spoiler_text_map" => %{"a" => "mew", "b" => "lol"},
|
||||
"sensitive" => "0"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"error" => _
|
||||
} = json_response_and_validate_schema(conn_one, 422)
|
||||
end
|
||||
|
||||
test "posting a multilang status, empty status_map", %{conn: conn} do
|
||||
idempotency_key = "Pikachu rocks!"
|
||||
|
||||
conn_one =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put_req_header("idempotency-key", idempotency_key)
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status_map" => %{},
|
||||
"spoiler_text_map" => %{"a" => "mew", "b" => "lol"},
|
||||
"sensitive" => "0"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"error" => _
|
||||
} = json_response_and_validate_schema(conn_one, 422)
|
||||
end
|
||||
|
||||
test "posting a multilang status, invalid language code in spoiler_text_map", %{conn: conn} do
|
||||
idempotency_key = "Pikachu rocks!"
|
||||
|
||||
conn_one =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put_req_header("idempotency-key", idempotency_key)
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status_map" => %{"a" => "mew mew", "b" => "lol lol"},
|
||||
"spoiler_text_map" => %{"a" => "mew", "b_" => "lol"},
|
||||
"sensitive" => "0"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"error" => _
|
||||
} = json_response_and_validate_schema(conn_one, 422)
|
||||
end
|
||||
|
||||
test "posting a multilang status, empty spoiler_text_map", %{conn: conn} do
|
||||
idempotency_key = "Pikachu rocks!"
|
||||
|
||||
conn_one =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put_req_header("idempotency-key", idempotency_key)
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status_map" => %{"a" => "mew mew", "b" => "lol lol"},
|
||||
"spoiler_text_map" => %{},
|
||||
"sensitive" => "0"
|
||||
})
|
||||
|
||||
assert %{
|
||||
"error" => _
|
||||
} = json_response_and_validate_schema(conn_one, 422)
|
||||
end
|
||||
|
||||
test "posting a multilang status with singlelang summary", %{conn: conn} do
|
||||
idempotency_key = "Pikachu rocks!"
|
||||
|
||||
|
@ -713,6 +785,44 @@ test "posting a multilang poll", %{conn: conn} do
|
|||
assert question.data["closed"] =~ "Z"
|
||||
end
|
||||
|
||||
test "posting a multilang poll, invalid lang code", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status" => "Who is the #bestgrill?",
|
||||
"poll" => %{
|
||||
"options_map" => [
|
||||
%{"a" => "Rei", "b" => "1"},
|
||||
%{"a" => "Asuka", "b_" => "2"},
|
||||
%{"a" => "Misato", "b" => "3"}
|
||||
],
|
||||
"expires_in" => 420
|
||||
}
|
||||
})
|
||||
|
||||
assert %{"error" => _} = json_response_and_validate_schema(conn, 422)
|
||||
end
|
||||
|
||||
test "posting a multilang poll, empty map", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/statuses", %{
|
||||
"status" => "Who is the #bestgrill?",
|
||||
"poll" => %{
|
||||
"options_map" => [
|
||||
%{"a" => "Rei", "b" => "1"},
|
||||
%{},
|
||||
%{"a" => "Misato", "b" => "3"}
|
||||
],
|
||||
"expires_in" => 420
|
||||
}
|
||||
})
|
||||
|
||||
assert %{"error" => _} = json_response_and_validate_schema(conn, 422)
|
||||
end
|
||||
|
||||
test "option limit is enforced", %{conn: conn} do
|
||||
limit = Config.get([:instance, :poll_limits, :max_options])
|
||||
|
||||
|
|
Loading…
Reference in a new issue