--- /dev/null
+defmodule Pleroma.PasswordResetToken do
+ use Ecto.Schema
+
+ import Ecto.{Changeset, Query}
+
+ alias Pleroma.{User, PasswordResetToken, Repo}
+
+ schema "password_reset_tokens" do
+ belongs_to :user, User
+ field :token, :string
+ field :used, :boolean, default: false
+
+ timestamps()
+ end
+
+ def create_token(%User{} = user) do
+ token = :crypto.strong_rand_bytes(32) |> Base.url_encode64
+
+ token = %PasswordResetToken{
+ user_id: user.id,
+ used: false,
+ token: token
+ }
+
+ Repo.insert(token)
+ end
+
+ def used_changeset(struct) do
+ changeset = struct
+ |> cast(%{}, [])
+ |> put_change(:used, true)
+ end
+
+ def reset_password(token, data) do
+ with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
+ %User{} = user <- Repo.get(User, token.user_id),
+ {:ok, user} <- User.reset_password(user, data),
+ {:ok, token} <- Repo.update(used_changeset(token)) do
+ {:ok, token}
+ else
+ _e -> {:error, token}
+ end
+ end
+end
|> validate_length(:name, min: 1, max: 100)
end
+ def password_update_changeset(struct, params) do
+ changeset = struct
+ |> cast(params, [:password, :password_confirmation])
+ |> validate_required([:password, :password_confirmation])
+ |> validate_confirmation(:password)
+
+ if changeset.valid? do
+ hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
+ changeset
+ |> put_change(:password_hash, hashed)
+ else
+ changeset
+ end
+ end
+
+ def reset_password(user, data) do
+ Repo.update(password_update_changeset(user, data))
+ end
+
def register_changeset(struct, params \\ %{}) do
changeset = struct
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
plug :accepts, ["html", "json"]
end
+ pipeline :password_reset do
+ plug :accepts, ["html"]
+ end
+
+ scope "/api/pleroma", Pleroma.Web.TwitterAPI do
+ pipe_through :password_reset
+ get "/password_reset/:token", UtilController, :show_password_reset
+ post "/password_reset", UtilController, :password_reset
+ end
+
scope "/oauth", Pleroma.Web.OAuth do
get "/authorize", OAuthController, :authorize
post "/authorize", OAuthController, :create_authorization
--- /dev/null
+<h2>Invalid Token</h2>
--- /dev/null
+<h2>Password Reset for <%= @user.nickname %></h2>
+<%= form_for @conn, util_path(@conn, :password_reset), [as: "data"], fn f -> %>
+<%= label f, :password, "Password" %>
+<%= password_input f, :password %>
+<br>
+
+<%= label f, :password_confirmation, "Confirmation" %>
+<%= password_input f, :password_confirmation %>
+<br>
+<%= hidden_input f, :token, value: @token.token %>
+<%= submit "Reset" %>
+<% end %>
--- /dev/null
+<h2>Password reset failed</h2>
--- /dev/null
+<h2>Password changed!</h2>
use Pleroma.Web, :controller
alias Pleroma.Web
+ alias Pleroma.{Repo, PasswordResetToken, User}
+
+ def show_password_reset(conn, %{"token" => token}) do
+ with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
+ %User{} = user <- Repo.get(User, token.user_id) do
+ render conn, "password_reset.html", %{
+ token: token,
+ user: user
+ }
+ else
+ _e -> render conn, "invalid_token.html"
+ end
+ end
+
+ def password_reset(conn, %{"data" => data}) do
+ with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
+ render conn, "password_reset_success.html"
+ else
+ _e -> render conn, "password_reset_failed.html"
+ end
+ end
+
def help_test(conn, _params) do
json(conn, "ok")
end
--- /dev/null
+defmodule Pleroma.Web.TwitterAPI.UtilView do
+ use Pleroma.Web, :view
+ import Phoenix.HTML.Form
+end
--- /dev/null
+defmodule Pleroma.Repo.Migrations.CreatePasswordResetTokens do
+ use Ecto.Migration
+
+ def change do
+ create table(:password_reset_tokens) do
+ add :token, :string
+ add :user_id, references(:users)
+ add :used, :boolean, default: false
+
+ timestamps()
+ end
+ end
+end