Mastodon API: Respect post privacy in favourited/reblogged endpoints
This commit is contained in:
		
							parent
							
								
									53a3ad6043
								
							
						
					
					
						commit
						056780fd8e
					
				
					 3 changed files with 56 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 | 
			
		|||
### Security
 | 
			
		||||
- OStatus: eliminate the possibility of a protocol downgrade attack.
 | 
			
		||||
- OStatus: prevent following locked accounts, bypassing the approval process.
 | 
			
		||||
- Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
 | 
			
		||||
 | 
			
		||||
### Removed
 | 
			
		||||
- **Breaking:** GNU Social API with Qvitter extensions support
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -842,6 +842,7 @@ def get_mascot(%{assigns: %{user: user}} = conn, _params) do
 | 
			
		|||
 | 
			
		||||
  def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
 | 
			
		||||
    with %Activity{} = activity <- Activity.get_by_id_with_object(id),
 | 
			
		||||
         {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
 | 
			
		||||
         %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
 | 
			
		||||
      q = from(u in User, where: u.ap_id in ^likes)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -853,12 +854,14 @@ def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
 | 
			
		|||
      |> put_view(AccountView)
 | 
			
		||||
      |> render("accounts.json", %{for: user, users: users, as: :user})
 | 
			
		||||
    else
 | 
			
		||||
      {:visible, false} -> {:error, :not_found}
 | 
			
		||||
      _ -> json(conn, [])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
 | 
			
		||||
    with %Activity{} = activity <- Activity.get_by_id_with_object(id),
 | 
			
		||||
         {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
 | 
			
		||||
         %Object{data: %{"announcements" => announces}} <- Object.normalize(activity) do
 | 
			
		||||
      q = from(u in User, where: u.ap_id in ^announces)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -870,6 +873,7 @@ def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
 | 
			
		|||
      |> put_view(AccountView)
 | 
			
		||||
      |> render("accounts.json", %{for: user, users: users, as: :user})
 | 
			
		||||
    else
 | 
			
		||||
      {:visible, false} -> {:error, :not_found}
 | 
			
		||||
      _ -> json(conn, [])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3698,7 +3698,7 @@ test "returns 404 when poll is private and not available for user", %{conn: conn
 | 
			
		|||
        build_conn()
 | 
			
		||||
        |> assign(:user, user)
 | 
			
		||||
 | 
			
		||||
      [conn: conn, activity: activity]
 | 
			
		||||
      [conn: conn, activity: activity, user: user]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "returns users who have favorited the status", %{conn: conn, activity: activity} do
 | 
			
		||||
| 
						 | 
				
			
			@ -3758,6 +3758,32 @@ test "does not fail on an unauthenticated request", %{conn: conn, activity: acti
 | 
			
		|||
      [%{"id" => id}] = response
 | 
			
		||||
      assert id == other_user.id
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "requires authentifucation for private posts", %{conn: conn, user: user} do
 | 
			
		||||
      other_user = insert(:user)
 | 
			
		||||
 | 
			
		||||
      {:ok, activity} =
 | 
			
		||||
        CommonAPI.post(user, %{
 | 
			
		||||
          "status" => "@#{other_user.nickname} wanna get some #cofe together?",
 | 
			
		||||
          "visibility" => "direct"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
 | 
			
		||||
 | 
			
		||||
      conn
 | 
			
		||||
      |> assign(:user, nil)
 | 
			
		||||
      |> get("/api/v1/statuses/#{activity.id}/favourited_by")
 | 
			
		||||
      |> json_response(404)
 | 
			
		||||
 | 
			
		||||
      response =
 | 
			
		||||
        build_conn()
 | 
			
		||||
        |> assign(:user, other_user)
 | 
			
		||||
        |> get("/api/v1/statuses/#{activity.id}/favourited_by")
 | 
			
		||||
        |> json_response(200)
 | 
			
		||||
 | 
			
		||||
      [%{"id" => id}] = response
 | 
			
		||||
      assert id == other_user.id
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "GET /api/v1/statuses/:id/reblogged_by" do
 | 
			
		||||
| 
						 | 
				
			
			@ -3769,7 +3795,7 @@ test "does not fail on an unauthenticated request", %{conn: conn, activity: acti
 | 
			
		|||
        build_conn()
 | 
			
		||||
        |> assign(:user, user)
 | 
			
		||||
 | 
			
		||||
      [conn: conn, activity: activity]
 | 
			
		||||
      [conn: conn, activity: activity, user: user]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
 | 
			
		||||
| 
						 | 
				
			
			@ -3829,6 +3855,29 @@ test "does not fail on an unauthenticated request", %{conn: conn, activity: acti
 | 
			
		|||
      [%{"id" => id}] = response
 | 
			
		||||
      assert id == other_user.id
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    test "requires authentifucation for private posts", %{conn: conn, user: user} do
 | 
			
		||||
      other_user = insert(:user)
 | 
			
		||||
 | 
			
		||||
      {:ok, activity} =
 | 
			
		||||
        CommonAPI.post(user, %{
 | 
			
		||||
          "status" => "@#{other_user.nickname} wanna get some #cofe together?",
 | 
			
		||||
          "visibility" => "direct"
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      conn
 | 
			
		||||
      |> assign(:user, nil)
 | 
			
		||||
      |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
 | 
			
		||||
      |> json_response(404)
 | 
			
		||||
 | 
			
		||||
      response =
 | 
			
		||||
        build_conn()
 | 
			
		||||
        |> assign(:user, other_user)
 | 
			
		||||
        |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
 | 
			
		||||
        |> json_response(200)
 | 
			
		||||
 | 
			
		||||
      assert [] == response
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "POST /auth/password, with valid parameters" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue