Compare commits

...

9 commits

Author SHA1 Message Date
Paweł Świątkowski df21b61829
Return last_status_at as date, not datetime 2024-02-05 21:42:15 +01:00
floatingghost e97d08ee98 Merge pull request 'MRF transparency: don’t forget to obfuscate short domains' (#676) from Oneric/akkoma:mrf-obfuscation into develop
Reviewed-on: AkkomaGang/akkoma#676
2024-02-05 08:43:43 +00:00
Oneric 3cd882528e More prominently document MRF transparency and obfuscation
And point to the cheat sheet for all other MRF policies
and their configuration details.
2024-02-02 14:50:21 +00:00
Oneric e47c50666d Fix obfuscation of short domains
Fixes AkkomaGang/akkoma#645
2024-02-02 14:50:13 +00:00
floatingghost b4ccddab39 Merge pull request 'Fix OAuth consumer mode' (#668) from tcmal/akkoma:develop into develop
Reviewed-on: AkkomaGang/akkoma#668
2024-02-02 10:05:42 +00:00
Aria 77000b8ffd update tests for oauth consumer 2023-12-17 21:48:19 +00:00
Aria a074be24ca add bit about frontend configuration to oauth consumer docs 2023-12-17 19:36:27 +00:00
Aria eb0dbf6b79 fix oauth consumer mode
the previous code passed a state parameter to ueberauth with info
about where to go after the user logged in, etc.
since ueberauth 0.7, this parameter is ignored and oauth state is used
for actual CSRF reasons.

we now set a cookie with the state we need to keep track of, and read
it once the callback happens.
2023-12-17 19:27:36 +00:00
Aria e2f749b5b0 don't select ueberauth 0.10.6, as it is broken
see https://github.com/ueberauth/ueberauth/issues/194
2023-12-17 18:59:31 +00:00
11 changed files with 121 additions and 39 deletions

View file

@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Documentation issue in which a non-existing nginx file was referenced
- Issue where a bad inbox URL could break federation
- Issue where hashtag rel values would be scrubbed
- Issue where short domains listed in `transparency_obfuscate_domains` were not actually obfuscated
## 2023.08

View file

@ -958,6 +958,15 @@ config :ueberauth, Ueberauth,
]
```
You may also need to set up your frontend to use oauth logins. For example, for `akkoma-fe`:
```elixir
config :pleroma, :frontend_configurations,
pleroma_fe: %{
loginMethod: "token"
}
```
## Link parsing
### :uri_schemes

View file

@ -61,6 +61,32 @@ config :pleroma, :mrf_simple,
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
## Hiding or Obfuscating Policies
You can opt out of publicly displaying all MRF policies or only hide or obfuscate selected domains.
To just hide everything set:
```elixir
config :pleroma, :mrf,
...
transparency: false,
```
To hide or obfuscate only select entries, use:
```elixir
config :pleroma, :mrf,
...
transparency_obfuscate_domains: ["handholdi.ng", "badword.com"],
transparency_exclusions: [{"ghost.club", "even a fragment is too spoopy for humans"}]
```
## More MRF Policies
See the [documentation cheatsheet](cheatsheet.md)
for all available MRF policies and their options.
## Writing your own MRF Policy
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `policies` config setting.

View file

@ -314,6 +314,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
def filter(object), do: {:ok, object}
defp obfuscate(string) when is_binary(string) do
# Want to strip at least two neighbouring chars
# to ensure at least one non-dot char is in the obfuscation area
stripped = String.length(string) - 6
{keepstart, keepend} =
if stripped > 1 do
{3, 3}
else
{
2 - div(1 - stripped, 2),
2 + div(stripped, 2)
}
end
string
|> to_charlist()
|> Enum.with_index()
@ -322,7 +336,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
?.
{char, index} ->
if 3 <= index && index < String.length(string) - 3, do: ?*, else: char
if keepstart <= index && index < String.length(string) - keepend, do: ?*, else: char
end)
|> to_string()
end

View file

@ -261,6 +261,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|> MediaProxy.url()
end
last_status_at =
if is_nil(user.last_status_at), do: nil, else: NaiveDateTime.to_date(user.last_status_at)
%{
id: to_string(user.id),
username: username_from_nickname(user.nickname),
@ -289,7 +292,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
actor_type: user.actor_type
}
},
last_status_at: user.last_status_at,
last_status_at: last_status_at,
akkoma: %{
instance: render("instance.json", %{instance: instance}),
status_ttl_days: user.status_ttl_days

View file

@ -39,6 +39,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
action_fallback(Pleroma.Web.OAuth.FallbackController)
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
@state_cookie_name "akkoma_oauth_state"
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do
@ -443,13 +444,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|> Map.put("scope", scope)
|> Jason.encode!()
params =
auth_attrs
|> Map.drop(~w(scope scopes client_id redirect_uri))
|> Map.put("state", state)
# Handing the request to Ueberauth
redirect(conn, to: ~p"/oauth/#{provider}?#{params}")
conn
|> put_resp_cookie(@state_cookie_name, state)
|> redirect(to: ~p"/oauth/#{provider}")
end
def request(%Plug.Conn{} = conn, params) do
@ -468,20 +466,26 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params) do
params = callback_params(params)
params = callback_params(conn, params)
messages = for e <- Map.get(failure, :errors, []), do: e.message
message = Enum.join(messages, "; ")
conn
|> put_flash(
:error,
dgettext("errors", "Failed to authenticate: %{message}.", message: message)
)
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
error_message = dgettext("errors", "Failed to authenticate: %{message}.", message: message)
if params["redirect_uri"] do
conn
|> put_flash(
:error,
error_message
)
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
else
send_resp(conn, :bad_request, error_message)
end
end
def callback(%Plug.Conn{} = conn, params) do
params = callback_params(params)
params = callback_params(conn, params)
with {:ok, registration} <- Authenticator.get_registration(conn) do
auth_attrs = Map.take(params, ~w(client_id redirect_uri scope scopes state))
@ -511,8 +515,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
defp callback_params(%{"state" => state} = params) do
Map.merge(params, Jason.decode!(state))
defp callback_params(%Plug.Conn{} = conn, params) do
fetch_cookies(conn)
Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, @state_cookie_name, "{}")))
end
def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do

View file

@ -156,7 +156,7 @@ defmodule Pleroma.Mixfile do
{:ex_syslogger, "~> 2.0.0"},
{:floki, "~> 0.34"},
{:timex, "~> 3.7"},
{:ueberauth, "~> 0.10"},
{:ueberauth, "== 0.10.5"},
{:linkify, git: "https://akkoma.dev/AkkomaGang/linkify.git"},
{:http_signatures,
git: "https://akkoma.dev/AkkomaGang/http_signatures.git",

View file

@ -124,7 +124,7 @@
"timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
"ueberauth": {:hex, :ueberauth, "0.10.6", "8dbefd5aec30c5830af2b6ce6e03f62cc28ae0757f34e2986454f54b8dca3f65", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "b0ad1c7508f3cfd5c2c1c668d1a32bafd77de4c56af82c7bfd7e54ed078a7928"},
"ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"},
"vex": {:hex, :vex, "0.9.1", "cb65348ebd1c4002861b65bef36e524c29d9a879c90119b2d0e674e323124277", [:mix], [], "hexpm", "a0f9f3959d127ad6a6a617c3f607ecfb1bc6f3c59f9c3614a901a46d1765bafe"},

View file

@ -283,7 +283,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert {:ok,
%{
mrf_simple: %{reject: ["rem***.*****nce", "a.b"]},
mrf_simple: %{reject: ["rem***.*****nce", "*.b"]},
mrf_simple_info: %{reject: %{"rem***.*****nce" => %{}}}
}} = SimplePolicy.describe()
end

View file

@ -40,7 +40,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
emoji: %{"karjalanpiirakka" => "/file.png"},
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"",
also_known_as: ["https://shitposter.zone/users/shp"],
status_ttl_days: 5
status_ttl_days: 5,
last_status_at: ~N[2023-12-31T15:06:17]
})
insert(:instance, %{host: "example.com", nodeinfo: %{version: "2.1"}})
@ -91,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
fields: []
},
fqn: "shp@shitposter.club",
last_status_at: nil,
last_status_at: ~D[2023-12-31],
pleroma: %{
ap_id: user.ap_id,
also_known_as: ["https://shitposter.zone/users/shp"],

View file

@ -81,9 +81,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
assert html_response(conn, 302)
redirect_query = URI.parse(redirected_to(conn)).query
assert %{"state" => state_param} = URI.decode_query(redirect_query)
assert {:ok, state_components} = Jason.decode(state_param)
assert {:ok, state_components} = Jason.decode(conn.resp_cookies["akkoma_oauth_state"].value)
expected_client_id = app.client_id
expected_redirect_uri = app.redirect_uris
@ -97,7 +95,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
end
test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
%{app: app, conn: conn} do
%{app: app, conn: _} do
registration = insert(:registration)
redirect_uri = OAuthController.default_redirect_uri(app)
@ -109,15 +107,17 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
}
conn =
conn
build_conn()
|> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params))
|> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session()
|> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid})
|> get(
"/oauth/twitter/callback",
%{
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
"provider" => "twitter",
"state" => Jason.encode!(state_params)
"provider" => "twitter"
}
)
@ -162,15 +162,42 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
app: app,
conn: conn
conn: _
} do
state_params = %{
"scope" => Enum.join(app.scopes, " "),
"client_id" => app.client_id,
"redirect_uri" => OAuthController.default_redirect_uri(app),
"state" => ""
"redirect_uri" => OAuthController.default_redirect_uri(app)
}
conn =
build_conn()
|> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params))
|> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session()
|> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
|> get(
"/oauth/twitter/callback",
%{
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
"provider" => "twitter",
"state" => ""
}
)
assert html_response(conn, 302)
assert redirected_to(conn) == app.redirect_uris
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
"Failed to authenticate: (error description)."
end
test "on authentication error with no prior state, GET /oauth/<provider>/callback returns 400",
%{
app: _,
conn: conn
} do
conn =
conn
|> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
@ -180,15 +207,11 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
"provider" => "twitter",
"state" => Jason.encode!(state_params)
"state" => ""
}
)
assert html_response(conn, 302)
assert redirected_to(conn) == app.redirect_uris
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
"Failed to authenticate: (error description)."
assert response(conn, 400)
end
test "GET /oauth/registration_details renders registration details form", %{