From 6af164f27b5a285fb1b1c8790a86db061c8fc28a Mon Sep 17 00:00:00 2001 From: Roger Braun Date: Thu, 19 Oct 2017 17:37:24 +0200 Subject: [PATCH] Add password reset. --- lib/pleroma/PasswordResetToken.ex | 44 +++++++++++++++++++ lib/pleroma/user.ex | 19 ++++++++ lib/pleroma/web/router.ex | 10 +++++ .../twitter_api/util/invalid_token.html.eex | 1 + .../twitter_api/util/password_reset.html.eex | 12 +++++ .../util/password_reset_failed.html.eex | 1 + .../util/password_reset_success.html.eex | 1 + .../controllers/util_controller.ex | 22 ++++++++++ .../web/twitter_api/views/util_view.ex | 4 ++ ...019141706_create_password_reset_tokens.exs | 13 ++++++ 10 files changed, 127 insertions(+) create mode 100644 lib/pleroma/PasswordResetToken.ex create mode 100644 lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex create mode 100644 lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex create mode 100644 lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex create mode 100644 lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex create mode 100644 lib/pleroma/web/twitter_api/views/util_view.ex create mode 100644 priv/repo/migrations/20171019141706_create_password_reset_tokens.exs diff --git a/lib/pleroma/PasswordResetToken.ex b/lib/pleroma/PasswordResetToken.ex new file mode 100644 index 000000000..79e60bf69 --- /dev/null +++ b/lib/pleroma/PasswordResetToken.ex @@ -0,0 +1,44 @@ +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 diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a04bbe276..938b57d90 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -97,6 +97,25 @@ def update_changeset(struct, params \\ %{}) do |> 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]) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6abf234c6..8497685a6 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -33,6 +33,16 @@ def user_fetcher(username) do 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 diff --git a/lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex b/lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex new file mode 100644 index 000000000..ee84750c7 --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/invalid_token.html.eex @@ -0,0 +1 @@ +

Invalid Token

diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex new file mode 100644 index 000000000..3c7960998 --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset.html.eex @@ -0,0 +1,12 @@ +

Password Reset for <%= @user.nickname %>

+<%= form_for @conn, util_path(@conn, :password_reset), [as: "data"], fn f -> %> +<%= label f, :password, "Password" %> +<%= password_input f, :password %> +
+ +<%= label f, :password_confirmation, "Confirmation" %> +<%= password_input f, :password_confirmation %> +
+<%= hidden_input f, :token, value: @token.token %> +<%= submit "Reset" %> +<% end %> diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex new file mode 100644 index 000000000..58a3736fd --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex @@ -0,0 +1 @@ +

Password reset failed

diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex new file mode 100644 index 000000000..c7dfcb6dd --- /dev/null +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex @@ -0,0 +1 @@ +

Password changed!

diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 32910d92c..11d8fa6c2 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -2,6 +2,28 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do 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 diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex new file mode 100644 index 000000000..71b04e6cc --- /dev/null +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -0,0 +1,4 @@ +defmodule Pleroma.Web.TwitterAPI.UtilView do + use Pleroma.Web, :view + import Phoenix.HTML.Form +end diff --git a/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs b/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs new file mode 100644 index 000000000..2d9be3aab --- /dev/null +++ b/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs @@ -0,0 +1,13 @@ +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