diff --git a/changelog.d/mrf-quietreply.add b/changelog.d/mrf-quietreply.add
new file mode 100644
index 000000000..4ed20bce6
--- /dev/null
+++ b/changelog.d/mrf-quietreply.add
@@ -0,0 +1 @@
+Added MRF.QuietReply which prevents replies to public posts from being published to the timelines
diff --git a/test/pleroma/web/o_auth/oauth_authorization_flow_test.exs b/test/pleroma/web/o_auth/oauth_authorization_flow_test.exs
deleted file mode 100644
index fdd8cbdb4..000000000
--- a/test/pleroma/web/o_auth/oauth_authorization_flow_test.exs
+++ /dev/null
@@ -1,339 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2022 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.OAuthAuthorizationFlowTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
-
- alias Pleroma.Helpers.AuthHelper
- alias Pleroma.Repo
- alias Pleroma.User
- alias Pleroma.Web.OAuth.App
- alias Pleroma.Web.OAuth.Authorization
- alias Pleroma.Web.OAuth.OAuthController
- alias Pleroma.Web.OAuth.Token
-
- @session_opts [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
-
- setup do
- clear_config([:instance, :account_activation_required], false)
- clear_config([:instance, :account_approval_required], false)
- end
-
- describe "OAuth authorization flow with external integration" do
- test "complete OAuth flow: create user, create app, authorize, get token, use token" do
- # Step 1: Create a user
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- # Step 2: Create a new OAuth client with the required scopes
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- redirect_uris: "urn:ietf:wg:oauth:2.0:oob"
- )
-
- # Step 3: Set up a logged in session
- conn =
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> AuthHelper.put_session_token(insert(:oauth_token, user: user).token)
-
- # Step 4: Access the /oauth/authorize endpoint with the specified parameters
- authorize_params = %{
- "client_id" => app.client_id,
- "response_type" => "code",
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "force_login" => "False",
- "state" => "None",
- "lang" => "None"
- }
-
- # First, get the authorization page
- conn = get(conn, "/oauth/authorize", authorize_params)
- assert html_response(conn, 200)
-
- # Step 5: Submit the authorization (simulate user approving the app)
- authorization_data = %{
- "authorization" => %{
- "client_id" => app.client_id,
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "state" => "None"
- }
- }
-
- conn = post(conn, "/oauth/authorize", authorization_data)
-
- # Should get the OOB authorization page with the code
- assert html_response(conn, 200)
-
- # Extract the authorization code from the response
- response = html_response(conn, 200)
- assert response =~ "Successfully authorized"
- assert response =~ "Token code is"
-
- # Parse the authorization code from the response
- code_match = Regex.run(~r/Token code is
([a-zA-Z0-9_-]+)/, response)
- assert code_match
- [_, authorization_code] = code_match
-
- # Step 6: Exchange the authorization code for an access token
- token_conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => authorization_code,
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- token_response = json_response(token_conn, 200)
- assert %{"access_token" => access_token, "token_type" => "Bearer"} = token_response
- assert token_response["scope"] == "read write follow push"
-
- # Verify the token was created in the database
- token_record = Repo.get_by(Token, token: access_token)
- assert token_record
- assert token_record.scopes == ["read", "write", "follow", "push"]
- assert token_record.user_id == user.id
- assert token_record.app_id == app.id
-
- # Step 7: Use the token to access a protected endpoint
- protected_conn =
- build_conn()
- |> put_req_header("authorization", "Bearer #{access_token}")
- |> get("/api/v1/accounts/verify_credentials")
-
- # Should get a 200 response with user information
- user_info = json_response(protected_conn, 200)
- assert user_info["id"] == to_string(user.id)
- assert user_info["username"] == user.nickname
- assert user_info["acct"] == user.nickname
-
- # Step 8: Test that the token has the correct scopes by accessing different endpoints
- # Test read:accounts scope (should work)
- conn_with_token =
- build_conn()
- |> put_req_header("authorization", "Bearer #{access_token}")
-
- # This should work because we have "read" scope
- conn_with_token
- |> get("/api/v1/accounts/#{user.id}")
- |> json_response(200)
-
- # Test write:accounts scope (should work) - with proper content-type
- conn_with_token
- |> put_req_header("content-type", "application/json")
- |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "Test Name"})
- |> json_response(200)
-
- # Test that the token is properly associated with the user
- assert token_record.user_id == user.id
- assert token_record.app_id == app.id
- end
-
- test "OAuth flow with force_login=false and existing session" do
- # Create a user and app
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- redirect_uris: "urn:ietf:wg:oauth:2.0:oob"
- )
-
- # Create an existing token for the same user and app
- existing_token = insert(:oauth_token, user: user, app: app, scopes: ["read", "write"])
-
- # Set up a logged in session with the existing token
- conn =
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> AuthHelper.put_session_token(existing_token.token)
-
- # Access the authorize endpoint with force_login=false
- authorize_params = %{
- "client_id" => app.client_id,
- "response_type" => "code",
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "force_login" => "False",
- "state" => "test_state"
- }
-
- # Should redirect to the OOB page with the existing token
- conn = get(conn, "/oauth/authorize", authorize_params)
- assert html_response(conn, 200)
- assert html_response(conn, 200) =~ "Authorization exists"
- end
-
- test "OAuth flow with different scopes than existing token" do
- # Create a user and app
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- redirect_uris: "urn:ietf:wg:oauth:2.0:oob"
- )
-
- # Create an existing token with different scopes
- existing_token = insert(:oauth_token, user: user, app: app, scopes: ["read"])
-
- # Set up a logged in session
- conn =
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> AuthHelper.put_session_token(existing_token.token)
-
- # Access the authorize endpoint requesting more scopes
- authorize_params = %{
- "client_id" => app.client_id,
- "response_type" => "code",
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "force_login" => "False",
- "state" => "test_state"
- }
-
- # Should show the authorization page because scopes are different
- conn = get(conn, "/oauth/authorize", authorize_params)
- assert html_response(conn, 200)
- assert html_response(conn, 200) =~ "Authorization exists"
- end
-
- test "OAuth flow with invalid client_id" do
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- conn =
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> AuthHelper.put_session_token(insert(:oauth_token, user: user).token)
-
- # Try to authorize with invalid client_id
- authorize_params = %{
- "client_id" => "invalid_client_id",
- "response_type" => "code",
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "force_login" => "False"
- }
-
- conn = get(conn, "/oauth/authorize", authorize_params)
- # Should still render the page but with error or missing app info
- assert html_response(conn, 200)
- end
-
- test "OAuth flow with unlisted redirect_uri" do
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- # Different from requested
- redirect_uris: "https://example.com/callback"
- )
-
- conn =
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> AuthHelper.put_session_token(insert(:oauth_token, user: user).token)
-
- # Try to authorize with unlisted redirect_uri
- authorize_params = %{
- "client_id" => app.client_id,
- "response_type" => "code",
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read write follow push",
- "force_login" => "False"
- }
-
- conn = get(conn, "/oauth/authorize", authorize_params)
- # Should still render the page but with error about unlisted redirect_uri
- assert html_response(conn, 200)
- end
-
- test "OAuth flow with expired authorization code" do
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- redirect_uris: "urn:ietf:wg:oauth:2.0:oob"
- )
-
- # Create an expired authorization
- expired_auth =
- insert(:oauth_authorization,
- user: user,
- app: app,
- # 1 hour ago
- valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -3600),
- scopes: ["read", "write", "follow", "push"]
- )
-
- # Try to exchange expired code for token
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => expired_auth.token,
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- # Should get an error
- response = json_response(conn, 400)
- assert %{"error" => _} = response
- end
-
- test "OAuth flow with used authorization code" do
- user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"))
-
- app =
- insert(:oauth_app,
- scopes: ["read", "write", "follow", "push"],
- redirect_uris: "urn:ietf:wg:oauth:2.0:oob"
- )
-
- # Create an authorization and mark it as used
- auth =
- insert(:oauth_authorization,
- user: user,
- app: app,
- scopes: ["read", "write", "follow", "push"]
- )
-
- {:ok, _} = Authorization.use_token(auth)
-
- # Try to exchange used code for token
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => auth.token,
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- # Should get an error
- response = json_response(conn, 400)
- assert %{"error" => _} = response
- end
- end
-end