@@ -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 |
@@ -97,6 +97,25 @@ defmodule Pleroma.User do | |||||
|> validate_length(:name, min: 1, max: 100) | |> validate_length(:name, min: 1, max: 100) | ||||
end | 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 | def register_changeset(struct, params \\ %{}) do | ||||
changeset = struct | changeset = struct | ||||
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) | |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) | ||||
@@ -33,6 +33,16 @@ defmodule Pleroma.Web.Router do | |||||
plug :accepts, ["html", "json"] | plug :accepts, ["html", "json"] | ||||
end | 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 | scope "/oauth", Pleroma.Web.OAuth do | ||||
get "/authorize", OAuthController, :authorize | get "/authorize", OAuthController, :authorize | ||||
post "/authorize", OAuthController, :create_authorization | post "/authorize", OAuthController, :create_authorization | ||||
@@ -0,0 +1 @@ | |||||
<h2>Invalid Token</h2> |
@@ -0,0 +1,12 @@ | |||||
<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 %> |
@@ -0,0 +1 @@ | |||||
<h2>Password reset failed</h2> |
@@ -0,0 +1 @@ | |||||
<h2>Password changed!</h2> |
@@ -2,6 +2,28 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do | |||||
use Pleroma.Web, :controller | use Pleroma.Web, :controller | ||||
alias Pleroma.Web | 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 | def help_test(conn, _params) do | ||||
json(conn, "ok") | json(conn, "ok") | ||||
end | end | ||||
@@ -0,0 +1,4 @@ | |||||
defmodule Pleroma.Web.TwitterAPI.UtilView do | |||||
use Pleroma.Web, :view | |||||
import Phoenix.HTML.Form | |||||
end |
@@ -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 |