## Unreleased
+### Changed
+
+- **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
+- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
+- Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
+
+### Added
+
+- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
+- Return OAuth token `id` (primary key) in POST `/oauth/token`.
+- `AnalyzeMetadata` upload filter for extracting attachment dimensions and generating blurhashes.
+- Attachment dimensions and blurhashes are federated when available.
+- Pinned posts federation
+
+### Fixed
+- Don't crash so hard when email settings are invalid.
+- Checking activated Upload Filters for required commands.
+ ### Removed
+ - **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
+
## Unreleased (Patch)
+### Fixed
+
+- Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable.
+- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image
+- Applying ConcurrentLimiter settings via AdminAPI
+- User login failures if their `notification_settings` were in a NULL state.
+- Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity
+- MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
+- Fixed some Markdown issues, including trailing slash in links.
+
## [2.3.0] - 2020-03-01
### Security
- **Breaking**: Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
- **Breaking**: Changed `mix pleroma.user toggle_activated` to `mix pleroma.user activate/deactivate`
+- **Breaking:** NSFW hashtag is no longer added on sensitive posts
- Polls now always return a `voters_count`, even if they are single-choice.
- Admin Emails: The ap id is used as the user link in emails now.
- Improved registration workflow for email confirmation and account approval modes.
- Pleroma API: Reroute `/api/pleroma/*` to `/api/v1/pleroma/*`
</details>
+- Improved hashtag timeline performance (requires a background migration).
### Added
- Static-FE: Fix remote posts not being sanitized
### Fixed
-=======
- Rate limiter crashes when there is no explicitly specified ip in the config
- 500 errors when no `Accept` header is present if Static-FE is enabled
- Instance panel not being updated immediately due to wrong `Cache-Control` headers
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
end
+ pipeline :static_fe do
+ plug(Pleroma.Web.Plugs.StaticFEPlug)
+ end
+
scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do
pipe_through(:pleroma_api)
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
-
- post(
- "/qvitter/statuses/notifications/read",
- TwitterAPI.Controller,
- :mark_notifications_as_read
- )
end
scope "/", Pleroma.Web do
# Note: html format is supported only if static FE is enabled
# Note: http signature is only considered for json requests (no auth for non-json requests)
- pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_json, :http_signature, :static_fe])
get("/objects/:uuid", OStatus.OStatusController, :object)
get("/activities/:uuid", OStatus.OStatusController, :activity)
scope "/", Pleroma.Web do
# Note: html format is supported only if static FE is enabled
# Note: http signature is only considered for json requests (no auth for non-json requests)
- pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_xml_json, :http_signature, :static_fe])
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
scope "/", Pleroma.Web do
# Note: html format is supported only if static FE is enabled
- pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_xml, :static_fe])
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
end
# The following two are S2S as well, see `ActivityPub.fetch_follow_information_for_user/1`:
get("/users/:nickname/followers", ActivityPubController, :followers)
get("/users/:nickname/following", ActivityPubController, :following)
+ get("/users/:nickname/collections/featured", ActivityPubController, :pinned)
end
scope "/", Pleroma.Web.ActivityPub do
get("/embed/:id", EmbedController, :show)
end
- scope "/proxy/", Pleroma.Web.MediaProxy do
- get("/preview/:sig/:url", MediaProxyController, :preview)
- get("/preview/:sig/:url/:filename", MediaProxyController, :preview)
- get("/:sig/:url", MediaProxyController, :remote)
- get("/:sig/:url/:filename", MediaProxyController, :remote)
+ scope "/proxy/", Pleroma.Web do
+ get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
+ get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
+ get("/:sig/:url", MediaProxy.MediaProxyController, :remote)
+ get("/:sig/:url/:filename", MediaProxy.MediaProxyController, :remote)
end
if Pleroma.Config.get(:env) == :dev do
options("/*path", RedirectController, :empty)
end
+
+ # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
+ def get_api_routes do
+ __MODULE__.__routes__()
+ |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
+ |> Enum.map(fn r ->
+ r.path
+ |> String.split("/", trim: true)
+ |> List.first()
+ end)
+ |> Enum.uniq()
+ end
end
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
"sensitive" => 0
})
+ # Idempotency plug response means detection fail
assert %{"id" => second_id} = json_response(conn_two, 200)
assert id == second_id
setup do: clear_config([:instance, :max_pinned_statuses], 1)
test "pin status", %{conn: conn, user: user, activity: activity} do
- id_str = to_string(activity.id)
+ id = activity.id
- assert %{"id" => ^id_str, "pinned" => true} =
+ assert %{"id" => ^id, "pinned" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/pin")
|> json_response_and_validate_schema(200)
- assert [%{"id" => ^id_str, "pinned" => true}] =
+ assert [%{"id" => ^id, "pinned" => true}] =
conn
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|> json_response_and_validate_schema(200)
end
+ test "non authenticated user", %{activity: activity} do
+ assert build_conn()
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses/#{activity.id}/pin")
+ |> json_response(403) == %{"error" => "Invalid credentials."}
+ end
+
test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
{:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{dm.id}/pin")
- assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"}
+ assert json_response_and_validate_schema(conn, 422) == %{
+ "error" => "Non-public status cannot be pinned"
+ }
+ end
+
+ test "pin by another user", %{activity: activity} do
+ %{conn: conn} = oauth_access(["write:accounts"])
+
+ assert conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses/#{activity.id}/pin")
+ |> json_response(422) == %{"error" => "Someone else's status cannot be pinned"}
end
test "unpin status", %{conn: conn, user: user, activity: activity} do
|> json_response_and_validate_schema(200)
end
- test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/statuses/1/unpin")
-
- assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"}
+ test "/unpin: returns 404 error when activity doesn't exist", %{conn: conn} do
+ assert conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses/1/unpin")
+ |> json_response_and_validate_schema(404) == %{"error" => "Record not found"}
end
test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
|> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"]))
|> get("api/v1/timelines/home")
- [reblogged_activity] = json_response(conn3, 200)
+ [reblogged_activity] = json_response_and_validate_schema(conn3, 200)
assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
"visibility" => "local"
})
- local = Pleroma.Constants.as_local_public()
+ local = Utils.as_local_public()
assert %{"content" => "cofe", "id" => id, "visibility" => "local"} =
- json_response(conn_one, 200)
+ json_response_and_validate_schema(conn_one, 200)
assert %Activity{id: ^id, data: %{"to" => [^local]}} = Activity.get_by_id(id)
end