mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2024-12-22 16:16:34 +00:00
Merge branch 'refactor-change-password' into 'develop'
LDAP: permit password changing See merge request pleroma/pleroma!4285
This commit is contained in:
commit
639016bdee
8 changed files with 121 additions and 46 deletions
1
changelog.d/ldap-password-change.add
Normal file
1
changelog.d/ldap-password-change.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
LDAP now supports users changing their passwords
|
7
installation/openldap/pw_self_service.ldif
Normal file
7
installation/openldap/pw_self_service.ldif
Normal file
|
@ -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
|
|
@ -15,6 +15,14 @@ defmodule Pleroma.LDAP do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
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
|
@impl true
|
||||||
def init(state) do
|
def init(state) do
|
||||||
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
|
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
|
||||||
|
@ -47,13 +55,42 @@ defmodule Pleroma.LDAP do
|
||||||
def handle_info(:connect, _state), do: do_handle_connect()
|
def handle_info(:connect, _state), do: do_handle_connect()
|
||||||
|
|
||||||
def handle_info({:bind_after_reconnect, name, password, from}, state) do
|
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)
|
GenServer.reply(from, result)
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_call({:bind_user, name, password}, from, state) 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}}
|
||||||
|
|
||||||
|
result ->
|
||||||
|
{:reply, result, state, :hibernate}
|
||||||
|
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)
|
||||||
|
|
||||||
|
if not is_nil(handle) do
|
||||||
|
:eldap.close(handle)
|
||||||
|
end
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
defp do_handle_connect do
|
defp do_handle_connect do
|
||||||
state =
|
state =
|
||||||
case connect() do
|
case connect() do
|
||||||
|
@ -71,33 +108,6 @@ defmodule Pleroma.LDAP do
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
|
||||||
def handle_call({:bind_user, name, password}, from, state) do
|
|
||||||
case bind_user(state[:handle], name, password) do
|
|
||||||
:needs_reconnect ->
|
|
||||||
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
|
|
||||||
{:noreply, state, {:continue, :connect}}
|
|
||||||
|
|
||||||
result ->
|
|
||||||
{:reply, result, state, :hibernate}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def terminate(_, state) do
|
|
||||||
handle = Keyword.get(state, :handle)
|
|
||||||
|
|
||||||
if not is_nil(handle) do
|
|
||||||
:eldap.close(handle)
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
def bind_user(name, password) do
|
|
||||||
GenServer.call(__MODULE__, {:bind_user, name, password})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp connect do
|
defp connect do
|
||||||
ldap = Config.get(:ldap, [])
|
ldap = Config.get(:ldap, [])
|
||||||
host = Keyword.get(ldap, :host, "localhost")
|
host = Keyword.get(ldap, :host, "localhost")
|
||||||
|
@ -161,18 +171,17 @@ defmodule Pleroma.LDAP do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp bind_user(handle, name, password) do
|
defp do_bind_user(handle, name, password) do
|
||||||
uid = Config.get([:ldap, :uid], "cn")
|
dn = make_dn(name)
|
||||||
base = Config.get([:ldap, :base])
|
|
||||||
|
|
||||||
case :eldap.simple_bind(handle, "#{uid}=#{name},#{base}", password) do
|
case :eldap.simple_bind(handle, dn, password) do
|
||||||
:ok ->
|
:ok ->
|
||||||
case fetch_user(name) do
|
case fetch_user(name) do
|
||||||
%User{} = user ->
|
%User{} = user ->
|
||||||
user
|
user
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
register_user(handle, base, uid, name)
|
register_user(handle, ldap_base(), ldap_uid(), name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# eldap does not inform us of socket closure
|
# eldap does not inform us of socket closure
|
||||||
|
@ -231,6 +240,14 @@ defmodule Pleroma.LDAP do
|
||||||
end
|
end
|
||||||
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
|
defp decode_certfile(file) do
|
||||||
with {:ok, data} <- File.read(file) do
|
with {:ok, data} <- File.read(file) do
|
||||||
data
|
data
|
||||||
|
@ -242,4 +259,13 @@ defmodule Pleroma.LDAP do
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do
|
||||||
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
||||||
@callback auth_template() :: String.t() | nil
|
@callback auth_template() :: String.t() | nil
|
||||||
@callback oauth_consumer_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
|
end
|
||||||
|
|
|
@ -30,4 +30,13 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
alias Pleroma.Web.Plugs.AuthenticationPlug
|
||||||
|
|
||||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||||
|
@ -101,4 +102,23 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
||||||
def auth_template, do: nil
|
def auth_template, do: nil
|
||||||
|
|
||||||
def oauth_consumer_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
|
end
|
||||||
|
|
|
@ -39,4 +39,8 @@ defmodule Pleroma.Web.Auth.WrapperAuthenticator do
|
||||||
implementation().oauth_consumer_template() ||
|
implementation().oauth_consumer_template() ||
|
||||||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Pleroma.Healthcheck
|
alias Pleroma.Healthcheck
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
|
@ -195,19 +196,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
|
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
|
with {:ok, %User{}} <-
|
||||||
{:ok, user} ->
|
Authenticator.change_password(
|
||||||
with {:ok, _user} <-
|
user,
|
||||||
User.reset_password(user, %{
|
body_params.password,
|
||||||
password: body_params.new_password,
|
body_params.new_password,
|
||||||
password_confirmation: body_params.new_password_confirmation
|
body_params.new_password_confirmation
|
||||||
}) do
|
) do
|
||||||
json(conn, %{status: "success"})
|
json(conn, %{status: "success"})
|
||||||
else
|
else
|
||||||
{:error, changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||||
json(conn, %{error: "New password #{error}."})
|
json(conn, %{error: "New password #{error}."})
|
||||||
end
|
|
||||||
|
{:error, :password_confirmation} ->
|
||||||
|
json(conn, %{error: "New password does not match confirmation."})
|
||||||
|
|
||||||
{:error, msg} ->
|
{:error, msg} ->
|
||||||
json(conn, %{error: msg})
|
json(conn, %{error: msg})
|
||||||
|
|
Loading…
Reference in a new issue