From 4d8159c9078e240e58a9849e8a35555a026d4a42 Mon Sep 17 00:00:00 2001 From: snewcomer Date: Sat, 29 Apr 2017 21:44:37 -0700 Subject: [PATCH] [RESET]: send token back after successful change of pwd close #796 close --- blueprint/api.apib | 31 ++++++++++++++++++ .../password_reset_controller_test.exs | 21 ++++++------ test/views/password_reset_view_test.exs | 6 ++-- web/controllers/password_reset_controller.ex | 32 +++++++------------ web/router.ex | 2 +- web/views/password_reset_view.ex | 4 +-- 6 files changed, 61 insertions(+), 35 deletions(-) diff --git a/blueprint/api.apib b/blueprint/api.apib index 71cf3eaf6..482bbffd6 100644 --- a/blueprint/api.apib +++ b/blueprint/api.apib @@ -500,6 +500,28 @@ This endpoint is a parent route for reset and forgot password endpoints + Response 200 (application/vnd.api+json; charset=utf-8) +## Reset [/password/reset] + +Reset Password resource allow a user to reset their password + +### Reset Password [POST] + ++ Request + + + Attributes (Reset Password Request) + ++ Response 201 (application/vnd.api+json; charset=utf-8) + + + Attributes (JSON Web Token Valid Response) + ++ Response 401 (application/vnd.api+json; charset=utf-8) + + + Attributes (JSON Web Token No User With Email Response) + ++ Response 401 (application/vnd.api+json; charset=utf-8) + + + Attributes (JSON Web Token Invalid Password Response) + # Group Previews Preview resources allow a user to create a temporary HTML preview for any kind of markdown. @@ -2850,6 +2872,15 @@ The platform stores Stripe customers and cards so they can be reused across diff + title: `404 Resource not found` (string) + status: 404 (number) - HTTP status code +## Reset Password Resource (object) ++ token: `zzz123` (string, required) ++ password: `mypwd` (string, required) ++ password-confirmation: `mypwd` (string, required) + +## Reset Password Request (object) ++ data(Reset Password Resource) ++ include JSON API Version + ## Role Skill Resource (object) + include Role Skill Resource Identifier + relationships diff --git a/test/controllers/password_reset_controller_test.exs b/test/controllers/password_reset_controller_test.exs index dbdfe13fa..7bafbc56e 100644 --- a/test/controllers/password_reset_controller_test.exs +++ b/test/controllers/password_reset_controller_test.exs @@ -2,31 +2,34 @@ defmodule CodeCorps.PasswordResetControllerTest do @moduledoc false use CodeCorps.ApiCase, resource_name: :password_reset - alias CodeCorps.AuthToken + alias CodeCorps.{User, AuthToken} - @tag :authenticated - test "creates and renders resource when data is valid", %{conn: conn, current_user: current_user} do + test "updates user password when data is valid", %{conn: conn} do + current_user = insert(:user) {:ok, auth_token} = AuthToken.changeset(%AuthToken{}, current_user) |> Repo.insert attrs = %{"token" => auth_token.value, "password" => "123456", "password_confirmation" => "123456"} conn = post conn, password_reset_path(conn, :reset_password), attrs response = json_response(conn, 201) assert response + encrypted_password = Repo.get(User, current_user.id).encrypted_password + assert Comeonin.Bcrypt.checkpw("123456", encrypted_password) end - @tag :authenticated - test "does not create resource and renders errors when password does not match", %{conn: conn, current_user: current_user} do + test "does not create resource and renders errors when password does not match", %{conn: conn} do + current_user = insert(:user) {:ok, auth_token} = AuthToken.changeset(%AuthToken{}, current_user) |> Repo.insert attrs = %{"token" => auth_token.value, "password" => "123456", "password_confirmation" => "another"} conn = post conn, password_reset_path(conn, :reset_password), attrs - assert json_response(conn, 422) + response = json_response(conn, 422) + assert %{"errors" => [%{"detail" => "Password confirmation passwords do not match"}]} = response end - @tag :authenticated - test "does not create resource and renders errors when token is invalid", %{conn: conn, current_user: current_user} do + test "does not create resource and renders errors when token is invalid", %{conn: conn} do + current_user = insert(:user) {:ok, _} = AuthToken.changeset(%AuthToken{}, current_user) |> Repo.insert attrs = %{"token" => "random token", "password" => "123456", "password_confirmation" => "123456"} conn = post conn, password_reset_path(conn, :reset_password), attrs - assert json_response(conn, 422) + assert json_response(conn, 404) end end diff --git a/test/views/password_reset_view_test.exs b/test/views/password_reset_view_test.exs index 2388ff48b..aae2dab9d 100644 --- a/test/views/password_reset_view_test.exs +++ b/test/views/password_reset_view_test.exs @@ -2,12 +2,12 @@ defmodule CodeCorps.PasswordResetViewTest do use CodeCorps.ViewCase test "renders show" do - email = "wat@codecorps.org" + token = "zzz123" - rendered_json = render(CodeCorps.PasswordResetView, "show.json", %{email: email}) + rendered_json = render(CodeCorps.PasswordResetView, "show.json", %{token: token}) expected_json = %{ - email: email + token: token } assert expected_json == rendered_json diff --git a/web/controllers/password_reset_controller.ex b/web/controllers/password_reset_controller.ex index 7e42de528..98d3a1c06 100644 --- a/web/controllers/password_reset_controller.ex +++ b/web/controllers/password_reset_controller.ex @@ -10,28 +10,20 @@ defmodule CodeCorps.PasswordResetController do 2. password & password_confirmation match and return email. 422 if pwd do not match or auth token does not exist """ - def reset_password(conn, %{"token" => token, "password" => password, "password_confirmation" => password_confirmation}) do - with %AuthToken{value: auth_token, user: user} <- Repo.get_by(AuthToken, %{ value: token }) |> Repo.preload(:user), - {:ok, _} <- Phoenix.Token.verify(CodeCorps.Endpoint, "user", auth_token, max_age: 1209600) do - with %Changeset{valid?: true} <- User.reset_password_changeset(user, - %{password: password, password_confirmation: password_confirmation}) do - conn - |> put_status(:created) - |> render("show.json", email: user.email) - else - %Changeset{valid?: false} -> - handle_reset_pswd_result(conn) - end + def reset_password(conn, %{"token" => reset_token, "password" => _password, "password_confirmation" => _password_confirmation} = params) do + with %AuthToken{user: user} <- AuthToken |> Repo.get_by(%{ value: reset_token }) |> Repo.preload(:user), + {:ok, _} <- Phoenix.Token.verify(CodeCorps.Endpoint, "user", reset_token, max_age: 1209600), + {:ok, updated_user} <- user |> User.reset_password_changeset(params) |> Repo.update, + {:ok, auth_token, _claims} = updated_user |> Guardian.encode_and_sign(:token) + do + conn + |> Plug.Conn.assign(:current_user, updated_user) + |> put_status(:created) + |> render("show.json", token: auth_token, user_id: updated_user.id, email: updated_user.email) else - nil -> - handle_reset_pswd_result(conn) + {:error, %Changeset{} = changeset} -> conn |> put_status(422) |> render(CodeCorps.ErrorView, :errors, data: changeset) + nil -> conn |> put_status(:not_found) |> render(CodeCorps.ErrorView, "404.json") end end - defp handle_reset_pswd_result(conn) do - conn - |> put_status(422) - |> render(CodeCorps.ErrorView, "422.json-api") - end - end diff --git a/web/router.ex b/web/router.ex index a2090b2ce..5613e99a2 100644 --- a/web/router.ex +++ b/web/router.ex @@ -65,7 +65,6 @@ defmodule CodeCorps.Router do resources "/donation-goals", DonationGoalController, only: [:create, :update, :delete] post "/github-connect", UserController, :github_connect resources "/organizations", OrganizationController, only: [:create, :update] - post "/password/reset", PasswordResetController, :reset_password resources "/previews", PreviewController, only: [:create] resources "/project-categories", ProjectCategoryController, only: [:create, :delete] resources "/project-skills", ProjectSkillController, only: [:create, :delete] @@ -93,6 +92,7 @@ defmodule CodeCorps.Router do post "/token", TokenController, :create post "/token/refresh", TokenController, :refresh + post "/password/reset", PasswordResetController, :reset_password resources "/categories", CategoryController, only: [:index, :show] resources "/comments", CommentController, only: [:index, :show] diff --git a/web/views/password_reset_view.ex b/web/views/password_reset_view.ex index c61d7dc7d..e320587de 100644 --- a/web/views/password_reset_view.ex +++ b/web/views/password_reset_view.ex @@ -1,9 +1,9 @@ defmodule CodeCorps.PasswordResetView do use CodeCorps.Web, :view - def render("show.json", %{email: email}) do + def render("show.json", %{token: token}) do %{ - email: email + token: token } end