[#114] Added :user_id component to email confirmation path to improve the security.
authorIvan Tashkinov <ivantashkinov@gmail.com>
Thu, 20 Dec 2018 10:41:30 +0000 (13:41 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Thu, 20 Dec 2018 10:41:30 +0000 (13:41 +0300)
Added tests for `confirm_email` action.

lib/pleroma/emails/user_email.ex
lib/pleroma/user.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
test/web/twitter_api/twitter_api_controller_test.exs

index 856816386e89708815f7796c374d210cac0400a0..8f916f47012cb4c616d5a12373f2b951fa4f4e7a 100644 (file)
@@ -70,6 +70,7 @@ defmodule Pleroma.UserEmail do
       Router.Helpers.confirm_email_url(
         Endpoint,
         :confirm_email,
+        user.id,
         to_string(user.info.confirmation_token)
       )
 
index 7e3a342f137e24cbcc0d74da557da1f75bec9392..ad50cf5582a6eed05ca2ef4b4779fc3fcde0c7f9 100644 (file)
@@ -396,10 +396,6 @@ defmodule Pleroma.User do
     end
   end
 
-  def get_by_confirmation_token(token) do
-    Repo.one(from(u in User, where: fragment("? ->> 'confirmation_token' = ?", u.info, ^token)))
-  end
-
   def get_followers_query(%User{id: id, follower_address: follower_address}) do
     from(
       u in User,
index ca069ab995e531a490d5213e4c54607a8b3c616f..d1c3b34f6bba00c47b31c871370683995bad00f8 100644 (file)
@@ -282,7 +282,12 @@ defmodule Pleroma.Web.Router do
     post("/account/register", TwitterAPI.Controller, :register)
     post("/account/password_reset", TwitterAPI.Controller, :password_reset)
 
-    get("/account/confirm_email/:token", TwitterAPI.Controller, :confirm_email, as: :confirm_email)
+    get(
+      "/account/confirm_email/:user_id/:token",
+      TwitterAPI.Controller,
+      :confirm_email,
+      as: :confirm_email
+    )
 
     post("/account/resend_confirmation_email", TwitterAPI.Controller, :resend_confirmation_email)
 
index 46dc9b12cc58fff114924700f971f6df668cd084..c644681b05fb047dd7f506c98a5f2f112227c2b8 100644 (file)
@@ -382,9 +382,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
     end
   end
 
-  def confirm_email(conn, %{"token" => token}) do
-    with %User{} = user <- User.get_by_confirmation_token(token),
+  def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
+    with %User{} = user <- Repo.get(User, uid),
          true <- user.local,
+         true <- user.info.confirmation_pending,
+         true <- user.info.confirmation_token == token,
          info_change <- User.Info.confirmation_changeset(user.info, :confirmed),
          changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
          {:ok, _} <- User.update_and_set_cache(changeset) do
index 53b39079308b07402948a5f7edc9be8dd8fecc9d..16422c35ad8508b884bec1d412eb67cac2aacca9 100644 (file)
@@ -873,7 +873,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
   end
 
-  describe "GET /api/account/confirm_email/:token" do
+  describe "GET /api/account/confirm_email/:id/:token" do
     setup do
       user = insert(:user)
       info_change = User.Info.confirmation_changeset(user.info, :unconfirmed)
@@ -890,19 +890,31 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
 
     test "it redirects to root url", %{conn: conn, user: user} do
-      conn = get(conn, "/api/account/confirm_email/#{user.info.confirmation_token}")
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}")
 
       assert 302 == conn.status
     end
 
     test "it confirms the user account", %{conn: conn, user: user} do
-      get(conn, "/api/account/confirm_email/#{user.info.confirmation_token}")
+      get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}")
 
       user = Repo.get(User, user.id)
 
       refute user.info.confirmation_pending
       refute user.info.confirmation_token
     end
+
+    test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/0/#{user.info.confirmation_token}")
+
+      assert 500 == conn.status
+    end
+
+    test "it returns 500 if token is invalid", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token")
+
+      assert 500 == conn.status
+    end
   end
 
   describe "POST /api/account/resend_confirmation_email" do