Merge branch 'develop' into feature/admin-api-user-statuses
authorMaxim Filippov <colixer@gmail.com>
Tue, 23 Jul 2019 23:42:28 +0000 (02:42 +0300)
committerMaxim Filippov <colixer@gmail.com>
Tue, 23 Jul 2019 23:42:28 +0000 (02:42 +0300)
1  2 
CHANGELOG.md
docs/api/admin_api.md
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/router.ex
test/support/factory.ex
test/web/admin_api/admin_api_controller_test.exs

diff --combined CHANGELOG.md
index 6c9381b4573d6f666d7fe4cf35fd62abb301e821,4043b1edfc848f3e0a3517ea79b7e65fd794961b..be0812760b41f3e3adf443f9b489591ccd262059
@@@ -11,7 -11,6 +11,7 @@@ The format is based on [Keep a Changelo
  - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set
  - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option
  - Mastodon API: Unsubscribe followers when they unfollow a user
 +- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
  
  ### Fixed
  - Not being able to pin unlisted posts
  - Mastodon API: Handling of search timeouts (`/api/v1/search` and `/api/v2/search`)
  - Mastodon API: Embedded relationships not being properly rendered in the Account entity of Status entity
  - Mastodon API: Add `account_id`, `type`, `offset`, and `limit` to search API (`/api/v1/search` and `/api/v2/search`)
+ - Mastodon API, streaming: Fix filtering of notifications based on blocks/mutes/thread mutes
  - ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
+ - Existing user id not being preserved on insert conflict
+ - Rich Media: Parser failing when no TTL can be found by image TTL setters
+ - ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
  
  ### Added
  - MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
  - MRF: Support for excluding specific domains from Transparency.
  - MRF: Support for filtering posts based on who they mention (`Pleroma.Web.ActivityPub.MRF.MentionPolicy`)
+ - MRF (Simple Policy): Support for wildcard domains.
+ - Support for wildcard domains in user domain blocks setting.
+ - Configuration: `quarantined_instances` support wildcard domains.
  - Configuration: `federation_incoming_replies_max_depth` option
  - Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
  - Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
@@@ -35,6 -41,7 +42,7 @@@
  - Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`). <https://github.com/tootsuite/mastodon/pull/10373>
  - Mastodon API: Add `pleroma.deactivated` to the Account entity
  - Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
+ - Mastodon API: /api/v1/accounts/:id/statuses now supports nicknames or user id
  - Admin API: Return users' tags when querying reports
  - Admin API: Return avatar and display name when querying users
  - Admin API: Allow querying user by ID
  - Twitter API: added rate limit for `/api/account/password_reset` endpoint.
  - ActivityPub: Add an internal service actor for fetching ActivityPub objects.
  - ActivityPub: Optional signing of ActivityPub object fetches.
 +- Admin API: Endpoint for fetching latest user's statuses
  
  ### Changed
  - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
  - Admin API: changed json structure for saving config settings.
  - RichMedia: parsers and their order are configured in `rich_media` config.
+ - RichMedia: add the rich media ttl based on image expiration time.
  
  ## [1.0.1] - 2019-07-14
  ### Security
diff --combined docs/api/admin_api.md
index 98968c1a69ca229406e3bfdc0246bccc972d6392,5ac3535c4fa842c63896c3a6942c577f3360d05c..ca930322794b04c40724f40b170d9bc1ed4b3b5f
@@@ -187,19 -187,6 +187,19 @@@ Note: Available `:permission_group` is 
    - On failure: `Not found`
    - On success: JSON of the user
  
 +## `/api/pleroma/admin/users/:nickname_or_id/statuses`
 +
 +### Retrive user's latest statuses
 +
 +- Method: `GET`
 +- Params:
 +  - `nickname` or `id`
 +  - *optional* `page_size`: number of statuses to return (default is `20`)
 +  - *optional* `godmode`: `true`/`false` – allows to see private statuses
 +- Response:
 +  - On failure: `Not found`
 +  - On success: JSON array of user's latest statuses
 +
  ## `/api/pleroma/admin/relay`
  
  ### Follow a Relay
  
  ## `/api/pleroma/admin/config`
  ### List config settings
+ List config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
  - Method `GET`
  - Params: none
  - Response:
  
  ## `/api/pleroma/admin/config`
  ### Update config settings
+ Updating config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
  Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
  Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
  Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
index 5c64bb81b6d98a7bcf6d9ab5cd625987642752f1,811be1effae486177a8057380543b1b2bee80a7a..1ae5acd91c0982267a8e2881c58a449559033ee3
@@@ -82,25 -82,6 +82,25 @@@ defmodule Pleroma.Web.AdminAPI.AdminAPI
      end
    end
  
 +  def list_user_statuses(conn, %{"nickname" => nickname} = params) do
 +    godmode = params["godmode"] == "true" || params["godmode"] == true
 +
 +    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
 +      {_, page_size} = page_params(params)
 +
 +      activities =
 +        ActivityPub.fetch_user_activities(user, nil, %{
 +          "limit" => page_size,
 +          "godmode" => godmode
 +        })
 +
 +      conn
 +      |> json(StatusView.render("index.json", %{activities: activities, as: :activity}))
 +    else
 +      _ -> {:error, :not_found}
 +    end
 +  end
 +
    def user_toggle_activation(conn, %{"nickname" => nickname}) do
      user = User.get_cached_by_nickname(nickname)
  
  
    @doc "Revokes invite by token"
    def revoke_invite(conn, %{"token" => token}) do
-     invite = UserInviteToken.find_by_token!(token)
-     {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true})
-     conn
-     |> json(AccountView.render("invite.json", %{invite: updated_invite}))
+     with {:ok, invite} <- UserInviteToken.find_by_token(token),
+          {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
+       conn
+       |> json(AccountView.render("invite.json", %{invite: updated_invite}))
+     else
+       nil -> {:error, :not_found}
+     end
    end
  
    @doc "Get a password reset token (base64 string) for given nickname"
index 3d92496016be5168f863c6f221ca4978f7518bbd,d230788d07c1c6b67cd17b467779bdcbe809bc77..a9f3826fc90cd595e9b52b008669549ac62e8993
@@@ -154,22 -154,12 +154,12 @@@ defmodule Pleroma.Web.Router d
      post("/users/follow", AdminAPIController, :user_follow)
      post("/users/unfollow", AdminAPIController, :user_unfollow)
  
-     # TODO: to be removed at version 1.0
-     delete("/user", AdminAPIController, :user_delete)
-     post("/user", AdminAPIController, :user_create)
      delete("/users", AdminAPIController, :user_delete)
      post("/users", AdminAPIController, :user_create)
      patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
      put("/users/tag", AdminAPIController, :tag_users)
      delete("/users/tag", AdminAPIController, :untag_users)
  
-     # TODO: to be removed at version 1.0
-     get("/permission_group/:nickname", AdminAPIController, :right_get)
-     get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
-     post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add)
-     delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete)
      get("/users/:nickname/permission_group", AdminAPIController, :right_get)
      get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
      post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add)
      post("/users/revoke_invite", AdminAPIController, :revoke_invite)
      post("/users/email_invite", AdminAPIController, :email_invite)
  
-     # TODO: to be removed at version 1.0
-     get("/password_reset", AdminAPIController, :get_password_reset)
      get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
  
      get("/users", AdminAPIController, :list_users)
      get("/users/:nickname", AdminAPIController, :user_show)
 +    get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
  
      get("/reports", AdminAPIController, :list_reports)
      get("/reports/:id", AdminAPIController, :report_show)
    pipeline :activitypub do
      plug(:accepts, ["activity+json", "json"])
      plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
+     plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
    end
  
    scope "/", Pleroma.Web.ActivityPub do
      end
    end
  
+   scope "/", Pleroma.Web.ActivityPub do
+     pipe_through(:activitypub)
+     post("/inbox", ActivityPubController, :inbox)
+     post("/users/:nickname/inbox", ActivityPubController, :inbox)
+   end
    scope "/relay", Pleroma.Web.ActivityPub do
      pipe_through(:ap_service_actor)
  
      post("/inbox", ActivityPubController, :inbox)
    end
  
-   scope "/", Pleroma.Web.ActivityPub do
-     pipe_through(:activitypub)
-     post("/inbox", ActivityPubController, :inbox)
-     post("/users/:nickname/inbox", ActivityPubController, :inbox)
-   end
    scope "/.well-known", Pleroma.Web do
      pipe_through(:well_known)
  
diff --combined test/support/factory.ex
index d02bd9212ccb74f969bf1bcfd486c22f7d2fef51,1f4239213cadc877ed181782013f318f60826cf5..c751546ce46bbf4edc6aedd14b824b29f4f3286c
@@@ -118,18 -118,20 +118,21 @@@ defmodule Pleroma.Factory d
    def note_activity_factory(attrs \\ %{}) do
      user = attrs[:user] || insert(:user)
      note = attrs[:note] || insert(:note, user: user)
-     published = attrs[:published] || DateTime.utc_now() |> DateTime.to_iso8601()
-     attrs = Map.drop(attrs, [:user, :note])
 +
-     data = %{
-       "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
-       "type" => "Create",
-       "actor" => note.data["actor"],
-       "to" => note.data["to"],
-       "object" => note.data["id"],
-       "published" => published,
-       "context" => note.data["context"]
-     }
+     data_attrs = attrs[:data_attrs] || %{}
+     attrs = Map.drop(attrs, [:user, :note, :data_attrs])
+     data =
+       %{
+         "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
+         "type" => "Create",
+         "actor" => note.data["actor"],
+         "to" => note.data["to"],
+         "object" => note.data["id"],
+         "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
+         "context" => note.data["context"]
+       }
+       |> Map.merge(data_attrs)
  
      %Pleroma.Activity{
        data: data,
index 20d5268a2b7065d730b970743cd14c534afabb38,1306c341df52b45d7ffc2bdf9a84dc0461e08137..6dda4ae5108868f7a834780964f0519fd255078b
@@@ -1010,6 -1010,17 +1010,17 @@@ defmodule Pleroma.Web.AdminAPI.AdminAPI
                 "uses" => 0
               }
      end
+     test "with invalid token" do
+       admin = insert(:user, info: %{is_admin: true})
+       conn =
+         build_conn()
+         |> assign(:user, admin)
+         |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
+       assert json_response(conn, :not_found) == "Not found"
+     end
    end
  
    describe "GET /api/pleroma/admin/reports/:id" do
                  %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
                  %{"tuple" => [":seconds_valid", 60]},
                  %{"tuple" => [":path", ""]},
-                 %{"tuple" => [":key1", nil]}
+                 %{"tuple" => [":key1", nil]},
+                 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}
                ]
              }
            ]
                       %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
                       %{"tuple" => [":seconds_valid", 60]},
                       %{"tuple" => [":path", ""]},
-                      %{"tuple" => [":key1", nil]}
+                      %{"tuple" => [":key1", nil]},
+                      %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}
                     ]
                   }
                 ]
               }
      end
    end
 +
 +  describe "GET /api/pleroma/admin/users/:nickname/statuses" do
 +    setup do
 +      admin = insert(:user, info: %{is_admin: true})
 +      user = insert(:user)
 +
 +      date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
 +      date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
 +      date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
 +
 +      insert(:note_activity, user: user, published: date1)
 +      insert(:note_activity, user: user, published: date2)
 +      insert(:note_activity, user: user, published: date3)
 +
 +      conn =
 +        build_conn()
 +        |> assign(:user, admin)
 +
 +      {:ok, conn: conn, user: user}
 +    end
 +
 +    test "renders user's statuses", %{conn: conn, user: user} do
 +      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
 +
 +      assert json_response(conn, 200) |> length() == 3
 +    end
 +
 +    test "renders user's statuses with a limit", %{conn: conn, user: user} do
 +      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
 +
 +      assert json_response(conn, 200) |> length() == 2
 +    end
 +
 +    test "doesn't return private statuses by default", %{conn: conn, user: user} do
 +      {:ok, _private_status} =
 +        CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
 +
 +      {:ok, _public_status} =
 +        CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
 +
 +      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
 +
 +      assert json_response(conn, 200) |> length() == 4
 +    end
 +
 +    test "returns private statuses with godmode on", %{conn: conn, user: user} do
 +      {:ok, _private_status} =
 +        CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
 +
 +      {:ok, _public_status} =
 +        CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
 +
 +      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
 +
 +      assert json_response(conn, 200) |> length() == 5
 +    end
 +  end
  end
  
  # Needed for testing