Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into fork
Browse files Browse the repository at this point in the history
Signed-off-by: marcin mikołajczak <[email protected]>
  • Loading branch information
mkljczk committed Oct 12, 2024
2 parents de1b53c + 639016b commit 95edffd
Show file tree
Hide file tree
Showing 21 changed files with 327 additions and 56 deletions.
1 change: 1 addition & 0 deletions changelog.d/hashtag-feeds-restricted.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Repesct :restrict_unauthenticated for hashtag rss/atom feeds
1 change: 1 addition & 0 deletions changelog.d/incoming-blocks.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix incoming Block activities being rejected
1 change: 1 addition & 0 deletions changelog.d/ldap-password-change.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LDAP now supports users changing their passwords
1 change: 1 addition & 0 deletions changelog.d/remote-report-policy.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added RemoteReportPolicy from Rebased for handling bogus federated reports
7 changes: 7 additions & 0 deletions installation/openldap/pw_self_service.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {1}to attrs=userPassword
by self write
by anonymous auth
by * none
5 changes: 5 additions & 0 deletions lib/pleroma/constants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ defmodule Pleroma.Constants do

const(activity_types,
do: [
"Block",
"Create",
"Update",
"Delete",
Expand Down Expand Up @@ -130,6 +131,10 @@ defmodule Pleroma.Constants do
]
)

const(object_types,
do: ~w[Event Question Answer Audio Video Image Article Note Page ChatMessage]
)

# basic regex, just there to weed out potential mistakes
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
const(mime_regex,
Expand Down
78 changes: 52 additions & 26 deletions lib/pleroma/ldap.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ defmodule Pleroma.LDAP do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def bind_user(name, password) do
GenServer.call(__MODULE__, {:bind_user, name, password})
end

def change_password(name, password, new_password) do
GenServer.call(__MODULE__, {:change_password, name, password, new_password})
end

@impl true
def init(state) do
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
Expand Down Expand Up @@ -47,33 +55,16 @@ defmodule Pleroma.LDAP do
def handle_info(:connect, _state), do: do_handle_connect()

def handle_info({:bind_after_reconnect, name, password, from}, state) do
result = bind_user(state[:handle], name, password)
result = do_bind_user(state[:handle], name, password)

GenServer.reply(from, result)

{:noreply, state}
end

defp do_handle_connect do
state =
case connect() do
{:ok, handle} ->
:eldap.controlling_process(handle, self())
Process.link(handle)
[handle: handle]

_ ->
Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
Process.send_after(self(), :connect, 5_000)
[]
end

{:noreply, state}
end

@impl true
def handle_call({:bind_user, name, password}, from, state) do
case bind_user(state[:handle], name, password) do
case do_bind_user(state[:handle], name, password) do
:needs_reconnect ->
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
{:noreply, state, {:continue, :connect}}
Expand All @@ -83,6 +74,12 @@ defmodule Pleroma.LDAP do
end
end

def handle_call({:change_password, name, password, new_password}, _from, state) do
result = change_password(state[:handle], name, password, new_password)

{:reply, result, state, :hibernate}
end

@impl true
def terminate(_, state) do
handle = Keyword.get(state, :handle)
Expand All @@ -94,8 +91,21 @@ defmodule Pleroma.LDAP do
:ok
end

def bind_user(name, password) do
GenServer.call(__MODULE__, {:bind_user, name, password})
defp do_handle_connect do
state =
case connect() do
{:ok, handle} ->
:eldap.controlling_process(handle, self())
Process.link(handle)
[handle: handle]

_ ->
Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
Process.send_after(self(), :connect, 5_000)
[]
end

{:noreply, state}
end

defp connect do
Expand Down Expand Up @@ -161,18 +171,17 @@ defmodule Pleroma.LDAP do
end
end

defp bind_user(handle, name, password) do
uid = Config.get([:ldap, :uid], "cn")
base = Config.get([:ldap, :base])
defp do_bind_user(handle, name, password) do
dn = make_dn(name)

case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do
case :eldap.simple_bind(handle, dn, password) do
:ok ->
case fetch_user(name) do
%User{} = user ->
user

_ ->
register_user(handle, base, uid, name)
register_user(handle, ldap_base(), ldap_uid(), name)
end

# eldap does not inform us of socket closure
Expand Down Expand Up @@ -231,6 +240,14 @@ defmodule Pleroma.LDAP do
end
end

defp change_password(handle, name, password, new_password) do
dn = make_dn(name)

with :ok <- :eldap.simple_bind(handle, dn, password) do
:eldap.modify_password(handle, dn, to_charlist(new_password), to_charlist(password))
end
end

defp decode_certfile(file) do
with {:ok, data} <- File.read(file) do
data
Expand All @@ -242,4 +259,13 @@ defmodule Pleroma.LDAP do
[]
end
end

defp ldap_uid, do: to_charlist(Config.get([:ldap, :uid], "cn"))
defp ldap_base, do: to_charlist(Config.get([:ldap, :base]))

defp make_dn(name) do
uid = ldap_uid()
base = ldap_base()
~c"#{uid}=#{name},#{base}"
end
end
23 changes: 23 additions & 0 deletions lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
with {_, false} <- {:local, local?(object)},
{:ok, _} <- maybe_reject_all(object),
{:ok, _} <- maybe_reject_anonymous(object),
{:ok, _} <- maybe_reject_third_party(object),
{:ok, _} <- maybe_reject_empty_message(object) do
{:ok, object}
else
Expand Down Expand Up @@ -37,6 +38,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
end
end

defp maybe_reject_third_party(%{"object" => objects} = object) do
{_, to} =
case objects do
[head | tail] when is_binary(head) -> {tail, head}
s when is_binary(s) -> {[], s}
_ -> {[], ""}
end

with true <- Config.get([:mrf_remote_report, :reject_third_party]),
false <- String.starts_with?(to, Pleroma.Web.Endpoint.url()) do
{:reject, "[RemoteReportPolicy] Third-party: #{to}"}
else
_ -> {:ok, object}
end
end

defp maybe_reject_empty_message(%{"content" => content} = object)
when is_binary(content) and content != "" do
{:ok, object}
Expand Down Expand Up @@ -83,6 +100,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
description: "Reject anonymous remote reports?",
suggestions: [true]
},
%{
key: :reject_third_party,
type: :boolean,
description: "Reject reports on users from third-party instances?",
suggestions: [true]
},
%{
key: :reject_empty_message,
type: :boolean,
Expand Down
12 changes: 12 additions & 0 deletions lib/pleroma/web/activity_pub/object_validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do

@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating

import Pleroma.Constants, only: [activity_types: 0, object_types: 0]

alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
Expand Down Expand Up @@ -42,6 +44,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@impl true
def validate(object, meta)

# This overload works together with the InboxGuardPlug
# and ensures that we are not accepting any activity type
# that cannot pass InboxGuardPlug.
# If we want to support any more activity types, make sure to
# add it in Pleroma.Constants's activity_types or object_types,
# and, if applicable, allowed_activity_types_from_strangers.
def validate(%{"type" => type}, _meta)
when type not in activity_types() and type not in object_types(),
do: {:error, :not_allowed_object_type}

def validate(%{"type" => "Block"} = block_activity, meta) do
with {:ok, block_activity} <-
block_activity
Expand Down
5 changes: 5 additions & 0 deletions lib/pleroma/web/auth/authenticator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do
@callback handle_error(Plug.Conn.t(), any()) :: any()
@callback auth_template() :: String.t() | nil
@callback oauth_consumer_template() :: String.t() | nil

@callback change_password(Pleroma.User.t(), String.t(), String.t(), String.t()) ::
{:ok, Pleroma.User.t()} | {:error, term()}

@optional_callbacks change_password: 4
end
9 changes: 9 additions & 0 deletions lib/pleroma/web/auth/ldap_authenticator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
error
end
end

def change_password(user, password, new_password, new_password) do
case LDAP.change_password(user.nickname, password, new_password) do
:ok -> {:ok, user}
e -> e
end
end

def change_password(_, _, _, _), do: {:error, :password_confirmation}
end
20 changes: 20 additions & 0 deletions lib/pleroma/web/auth/pleroma_authenticator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.AuthenticationPlug

import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
Expand Down Expand Up @@ -101,4 +102,23 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
def auth_template, do: nil

def oauth_consumer_template, do: nil

@doc "Changes Pleroma.User password in the database"
def change_password(user, password, new_password, new_password) do
case CommonAPI.Utils.confirm_current_password(user, password) do
{:ok, user} ->
with {:ok, _user} <-
User.reset_password(user, %{
password: new_password,
password_confirmation: new_password
}) do
{:ok, user}
end

error ->
error
end
end

def change_password(_, _, _, _), do: {:error, :password_confirmation}
end
4 changes: 4 additions & 0 deletions lib/pleroma/web/auth/wrapper_authenticator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator do
implementation().oauth_consumer_template() ||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
end

@impl true
def change_password(user, password, new_password, new_password_confirmation),
do: implementation().change_password(user, password, new_password, new_password_confirmation)
end
6 changes: 4 additions & 2 deletions lib/pleroma/web/feed/tag_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@ defmodule Pleroma.Web.Feed.TagController do
alias Pleroma.Web.Feed.FeedView

def feed(conn, params) do
if Config.get!([:instance, :public]) do
if not Config.restrict_unauthenticated_access?(:timelines, :local) do
render_feed(conn, params)
else
render_error(conn, :not_found, "Not found")
end
end

defp render_feed(conn, %{"tag" => raw_tag} = params) do
local_only = Config.restrict_unauthenticated_access?(:timelines, :federated)

{format, tag} = parse_tag(raw_tag)

activities =
%{type: ["Create"], tag: tag}
%{type: ["Create"], tag: tag, local_only: local_only}
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|> ActivityPub.fetch_public_activities()

Expand Down
29 changes: 16 additions & 13 deletions lib/pleroma/web/twitter_api/controllers/util_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Healthcheck
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.WebFinger
Expand Down Expand Up @@ -195,19 +196,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
_
) do
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
{:ok, user} ->
with {:ok, _user} <-
User.reset_password(user, %{
password: body_params.new_password,
password_confirmation: body_params.new_password_confirmation
}) do
json(conn, %{status: "success"})
else
{:error, changeset} ->
{_, {error, _}} = Enum.at(changeset.errors, 0)
json(conn, %{error: "New password #{error}."})
end
with {:ok, %User{}} <-
Authenticator.change_password(
user,
body_params.password,
body_params.new_password,
body_params.new_password_confirmation
) do
json(conn, %{status: "success"})
else
{:error, %Ecto.Changeset{} = changeset} ->
{_, {error, _}} = Enum.at(changeset.errors, 0)
json(conn, %{error: "New password #{error}."})

{:error, :password_confirmation} ->
json(conn, %{error: "New password does not match confirmation."})

{:error, msg} ->
json(conn, %{error: msg})
Expand Down
8 changes: 5 additions & 3 deletions lib/pleroma/workers/poll_worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ defmodule Pleroma.Workers.PollWorker do
def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)},
{:ok, notifications} <- Notification.create_poll_notifications(activity) do
# Schedule a final refresh
__MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id})
|> Oban.insert()
unless activity.local do
# Schedule a final refresh
__MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id})
|> Oban.insert()
end

Notification.stream(notifications)
else
Expand Down
Loading

0 comments on commit 95edffd

Please sign in to comment.