Repository: pypi/warehouse

Issue:
Error message is incorrect when attempting to add existing email
If I try to add an email address I've already added to my account, the error message says:

> This email address is already being used by another account. Use a different email.

Which isn't technically true, since the email address is already in use by the _current_ account. We should differentiate it and give a better message when this happens.

---

**Good First Issue**: This issue is good for first time contributors. If you've already contributed to Warehouse, work on [another issue without this label](https://github.com/pypa/warehouse/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+-label%3A%22good+first+issue%22) instead. If there is not a corresponding pull request for this issue, it is up for grabs. For directions for getting set up, see our [Getting Started Guide](https://warehouse.pypa.io/development/getting-started/). If you are working on this issue and have questions, feel free to ask them here, [`#pypa-dev` on Freenode](https://webchat.freenode.net/?channels=%23pypa-dev), or the [pypa-dev mailing list](https://groups.google.com/forum/#!forum/pypa-dev).



Discussion:

Resolving patch:
diff --git a/warehouse/accounts/forms.py b/warehouse/accounts/forms.py
--- a/warehouse/accounts/forms.py
+++ b/warehouse/accounts/forms.py
@@ -154,16 +154,24 @@ class NewEmailMixin:
     )
 
     def validate_email(self, field):
-        if self.user_service.find_userid_by_email(field.data) is not None:
+        userid = self.user_service.find_userid_by_email(field.data)
+
+        if userid and userid == self.user_id:
+            raise wtforms.validators.ValidationError(
+                f"This email address is already being used by this account. "
+                f"Use a different email."
+            )
+        if userid:
             raise wtforms.validators.ValidationError(
-                "This email address is already being used by another account. "
-                "Use a different email."
+                f"This email address is already being used by another account. "
+                f"Use a different email."
             )
+
         domain = field.data.split("@")[-1]
         if domain in disposable_email_domains.blacklist:
             raise wtforms.validators.ValidationError(
-                "You can't create an account with an email address "
-                "from this domain. Use a different email."
+                "You can't use an email address from this domain. Use a "
+                "different email."
             )
 
 
@@ -193,6 +201,7 @@ class RegistrationForm(
     def __init__(self, *args, user_service, **kwargs):
         super().__init__(*args, **kwargs)
         self.user_service = user_service
+        self.user_id = None
 
 
 class LoginForm(PasswordMixin, UsernameMixin, forms.Form):
diff --git a/warehouse/manage/forms.py b/warehouse/manage/forms.py
--- a/warehouse/manage/forms.py
+++ b/warehouse/manage/forms.py
@@ -68,9 +68,10 @@ class AddEmailForm(NewEmailMixin, forms.Form):
 
     __params__ = ["email"]
 
-    def __init__(self, *args, user_service, **kwargs):
+    def __init__(self, *args, user_service, user_id, **kwargs):
         super().__init__(*args, **kwargs)
         self.user_service = user_service
+        self.user_id = user_id
 
 
 class ChangePasswordForm(PasswordMixin, NewPasswordMixin, forms.Form):
diff --git a/warehouse/manage/views.py b/warehouse/manage/views.py
--- a/warehouse/manage/views.py
+++ b/warehouse/manage/views.py
@@ -106,7 +106,9 @@ def active_projects(self):
     def default_response(self):
         return {
             "save_account_form": SaveAccountForm(name=self.request.user.name),
-            "add_email_form": AddEmailForm(user_service=self.user_service),
+            "add_email_form": AddEmailForm(
+                user_service=self.user_service, user_id=self.request.user.id
+            ),
             "change_password_form": ChangePasswordForm(
                 user_service=self.user_service, breach_service=self.breach_service
             ),
@@ -129,7 +131,11 @@ def save_account(self):
 
     @view_config(request_method="POST", request_param=AddEmailForm.__params__)
     def add_email(self):
-        form = AddEmailForm(self.request.POST, user_service=self.user_service)
+        form = AddEmailForm(
+            self.request.POST,
+            user_service=self.user_service,
+            user_id=self.request.user.id,
+        )
 
         if form.validate():
             email = self.user_service.add_email(self.request.user.id, form.email.data)


Test patch:
diff --git a/tests/unit/accounts/test_forms.py b/tests/unit/accounts/test_forms.py
--- a/tests/unit/accounts/test_forms.py
+++ b/tests/unit/accounts/test_forms.py
@@ -336,8 +336,8 @@ def test_blacklisted_email_error(self):
         assert not form.validate()
         assert (
             form.email.errors.pop()
-            == "You can't create an account with an email address from "
-            "this domain. Use a different email."
+            == "You can't use an email address from this domain. Use a "
+            "different email."
         )
 
     def test_username_exists(self):
diff --git a/tests/unit/manage/test_forms.py b/tests/unit/manage/test_forms.py
--- a/tests/unit/manage/test_forms.py
+++ b/tests/unit/manage/test_forms.py
@@ -71,10 +71,53 @@ def test_validate_role_name_fails(self, value, expected):
 class TestAddEmailForm:
     def test_creation(self):
         user_service = pretend.stub()
-        form = forms.AddEmailForm(user_service=user_service)
+        form = forms.AddEmailForm(user_service=user_service, user_id=pretend.stub())
 
         assert form.user_service is user_service
 
+    def test_email_exists_error(self):
+        user_id = pretend.stub()
+        form = forms.AddEmailForm(
+            data={"email": "foo@bar.com"},
+            user_id=user_id,
+            user_service=pretend.stub(find_userid_by_email=lambda _: user_id),
+        )
+
+        assert not form.validate()
+        assert (
+            form.email.errors.pop()
+            == "This email address is already being used by this account. "
+            "Use a different email."
+        )
+
+    def test_email_exists_other_account_error(self):
+        form = forms.AddEmailForm(
+            data={"email": "foo@bar.com"},
+            user_id=pretend.stub(),
+            user_service=pretend.stub(find_userid_by_email=lambda _: pretend.stub()),
+        )
+
+        assert not form.validate()
+        assert (
+            form.email.errors.pop()
+            == "This email address is already being used by another account. "
+            "Use a different email."
+        )
+
+    def test_blacklisted_email_error(self):
+        form = forms.AddEmailForm(
+            data={"email": "foo@bearsarefuzzy.com"},
+            user_service=pretend.stub(find_userid_by_email=lambda _: None),
+            user_id=pretend.stub(),
+        )
+
+        assert not form.validate()
+        assert (
+            form.email.errors.pop()
+            == "You can't use an email address from this domain. "
+            "Use a different email."
+        )
+
 
 class TestChangePasswordForm:
     def test_creation(self):
diff --git a/tests/unit/manage/test_views.py b/tests/unit/manage/test_views.py
--- a/tests/unit/manage/test_views.py
+++ b/tests/unit/manage/test_views.py
@@ -47,12 +47,13 @@ def test_default_response(self, monkeypatch):
         breach_service = pretend.stub()
         user_service = pretend.stub()
         name = pretend.stub()
+        user_id = pretend.stub()
         request = pretend.stub(
             find_service=lambda iface, **kw: {
                 IPasswordBreachedService: breach_service,
                 IUserService: user_service,
             }[iface],
-            user=pretend.stub(name=name),
+            user=pretend.stub(name=name, id=user_id),
         )
         save_account_obj = pretend.stub()
         save_account_cls = pretend.call_recorder(lambda **kw: save_account_obj)
@@ -79,7 +80,9 @@ def test_default_response(self, monkeypatch):
         assert view.request == request
         assert view.user_service == user_service
         assert save_account_cls.calls == [pretend.call(name=name)]
-        assert add_email_cls.calls == [pretend.call(user_service=user_service)]
+        assert add_email_cls.calls == [
+            pretend.call(user_id=user_id, user_service=user_service)
+        ]
         assert change_pass_cls.calls == [
             pretend.call(user_service=user_service, breach_service=breach_service)
         ]
@@ -229,7 +232,7 @@ def test_add_email_validation_fails(self, monkeypatch):
             db=pretend.stub(flush=lambda: None),
             session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
             find_service=lambda a, **kw: pretend.stub(),
-            user=pretend.stub(emails=[], name=pretend.stub()),
+            user=pretend.stub(emails=[], name=pretend.stub(), id=pretend.stub()),
         )
         add_email_obj = pretend.stub(
             validate=lambda: False, email=pretend.stub(data=email_address)