Merge pull request 'Return HTTP code 413 when uploading an avatar/header that's too large' (#367) from norm/akkoma:return-413-max-size into develop
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/367
This commit is contained in:
		
						commit
						7f4d218cff
					
				
					 5 changed files with 106 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -6,11 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 | 
			
		|||
 | 
			
		||||
## Unreleased
 | 
			
		||||
 | 
			
		||||
## Removed
 | 
			
		||||
### Removed
 | 
			
		||||
- Non-finch HTTP adapters
 | 
			
		||||
 | 
			
		||||
## Upgrade notes
 | 
			
		||||
### Upgrade notes
 | 
			
		||||
- Ensure `config :tesla, :adapter` is either unset, or set to `{Tesla.Adapter.Finch, name: MyFinch}` in your .exs config
 | 
			
		||||
### Changed
 | 
			
		||||
- Return HTTP error 413 when uploading an avatar or banner that's above the configured upload limit instead of a 500.
 | 
			
		||||
 | 
			
		||||
## 2022.12
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -599,7 +599,13 @@ defp put_change_if_present(changeset, map_field, value_function) do
 | 
			
		|||
         {:ok, new_value} <- value_function.(value) do
 | 
			
		||||
      put_change(changeset, map_field, new_value)
 | 
			
		||||
    else
 | 
			
		||||
      _ -> changeset
 | 
			
		||||
      {:error, :file_too_large} ->
 | 
			
		||||
        Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value ->
 | 
			
		||||
          [{map_field, "file is too large"}]
 | 
			
		||||
        end)
 | 
			
		||||
 | 
			
		||||
      _ ->
 | 
			
		||||
        changeset
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,7 +64,8 @@ def update_credentials_operation do
 | 
			
		|||
      requestBody: request_body("Parameters", update_credentials_request(), required: true),
 | 
			
		||||
      responses: %{
 | 
			
		||||
        200 => Operation.response("Account", "application/json", Account),
 | 
			
		||||
        403 => Operation.response("Error", "application/json", ApiError)
 | 
			
		||||
        403 => Operation.response("Error", "application/json", ApiError),
 | 
			
		||||
        413 => Operation.response("Error", "application/json", ApiError)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -251,7 +251,17 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
 | 
			
		|||
        with_pleroma_settings: true
 | 
			
		||||
      )
 | 
			
		||||
    else
 | 
			
		||||
      _e -> render_error(conn, :forbidden, "Invalid request")
 | 
			
		||||
      {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} ->
 | 
			
		||||
        render_error(conn, :request_entity_too_large, "File is too large")
 | 
			
		||||
 | 
			
		||||
      {:error, %Ecto.Changeset{errors: [banner: {"file is too large", _}]}} ->
 | 
			
		||||
        render_error(conn, :request_entity_too_large, "File is too large")
 | 
			
		||||
 | 
			
		||||
      {:error, %Ecto.Changeset{errors: [background: {"file is too large", _}]}} ->
 | 
			
		||||
        render_error(conn, :request_entity_too_large, "File is too large")
 | 
			
		||||
 | 
			
		||||
      _e ->
 | 
			
		||||
        render_error(conn, :forbidden, "Invalid request")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -272,6 +272,34 @@ test "updates the user's avatar", %{user: user, conn: conn} do
 | 
			
		|||
      assert user.avatar == nil
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the user's avatar, upload_limit, returns a HTTP 413", %{conn: conn, user: user} do
 | 
			
		||||
      upload_limit = Config.get([:instance, :upload_limit]) * 8 + 8
 | 
			
		||||
 | 
			
		||||
      assert :ok ==
 | 
			
		||||
               File.write(Path.absname("test/tmp/large_binary.data"), <<0::size(upload_limit)>>)
 | 
			
		||||
 | 
			
		||||
      new_avatar_oversized = %Plug.Upload{
 | 
			
		||||
        content_type: nil,
 | 
			
		||||
        path: Path.absname("test/tmp/large_binary.data"),
 | 
			
		||||
        filename: "large_binary.data"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      assert user.avatar == %{}
 | 
			
		||||
 | 
			
		||||
      res =
 | 
			
		||||
        patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar_oversized})
 | 
			
		||||
 | 
			
		||||
      assert user_response = json_response_and_validate_schema(res, 413)
 | 
			
		||||
      assert user_response["avatar"] != User.avatar_url(user)
 | 
			
		||||
 | 
			
		||||
      user = User.get_by_id(user.id)
 | 
			
		||||
      assert user.avatar == %{}
 | 
			
		||||
 | 
			
		||||
      clear_config([:instance, :upload_limit], upload_limit)
 | 
			
		||||
 | 
			
		||||
      assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the user's banner", %{user: user, conn: conn} do
 | 
			
		||||
      new_header = %Plug.Upload{
 | 
			
		||||
        content_type: "image/jpeg",
 | 
			
		||||
| 
						 | 
				
			
			@ -291,6 +319,32 @@ test "updates the user's banner", %{user: user, conn: conn} do
 | 
			
		|||
      assert user.banner == nil
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the user's banner, upload_limit, returns a HTTP 413", %{conn: conn, user: user} do
 | 
			
		||||
      upload_limit = Config.get([:instance, :upload_limit]) * 8 + 8
 | 
			
		||||
 | 
			
		||||
      assert :ok ==
 | 
			
		||||
               File.write(Path.absname("test/tmp/large_binary.data"), <<0::size(upload_limit)>>)
 | 
			
		||||
 | 
			
		||||
      new_header_oversized = %Plug.Upload{
 | 
			
		||||
        content_type: nil,
 | 
			
		||||
        path: Path.absname("test/tmp/large_binary.data"),
 | 
			
		||||
        filename: "large_binary.data"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      res =
 | 
			
		||||
        patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header_oversized})
 | 
			
		||||
 | 
			
		||||
      assert user_response = json_response_and_validate_schema(res, 413)
 | 
			
		||||
      assert user_response["header"] != User.banner_url(user)
 | 
			
		||||
 | 
			
		||||
      user = User.get_by_id(user.id)
 | 
			
		||||
      assert user.banner == %{}
 | 
			
		||||
 | 
			
		||||
      clear_config([:instance, :upload_limit], upload_limit)
 | 
			
		||||
 | 
			
		||||
      assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the user's background", %{conn: conn, user: user} do
 | 
			
		||||
      new_header = %Plug.Upload{
 | 
			
		||||
        content_type: "image/jpeg",
 | 
			
		||||
| 
						 | 
				
			
			@ -314,6 +368,34 @@ test "updates the user's background", %{conn: conn, user: user} do
 | 
			
		|||
      assert user.background == nil
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "updates the user's background, upload_limit, returns a HTTP 413", %{
 | 
			
		||||
      conn: conn,
 | 
			
		||||
      user: user
 | 
			
		||||
    } do
 | 
			
		||||
      upload_limit = Config.get([:instance, :upload_limit]) * 8 + 8
 | 
			
		||||
 | 
			
		||||
      assert :ok ==
 | 
			
		||||
               File.write(Path.absname("test/tmp/large_binary.data"), <<0::size(upload_limit)>>)
 | 
			
		||||
 | 
			
		||||
      new_background_oversized = %Plug.Upload{
 | 
			
		||||
        content_type: nil,
 | 
			
		||||
        path: Path.absname("test/tmp/large_binary.data"),
 | 
			
		||||
        filename: "large_binary.data"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      res =
 | 
			
		||||
        patch(conn, "/api/v1/accounts/update_credentials", %{
 | 
			
		||||
          "pleroma_background_image" => new_background_oversized
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      assert user_response = json_response_and_validate_schema(res, 413)
 | 
			
		||||
      assert user.background == %{}
 | 
			
		||||
 | 
			
		||||
      clear_config([:instance, :upload_limit], upload_limit)
 | 
			
		||||
 | 
			
		||||
      assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "requires 'write:accounts' permission" do
 | 
			
		||||
      token1 = insert(:oauth_token, scopes: ["read"])
 | 
			
		||||
      token2 = insert(:oauth_token, scopes: ["write", "follow"])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue