auth: Rewrite DummyAuthBackend to not block email reuse.

This require some care to ensure we still provide the same nice error
messages for the case of a user who has an account, just not with this
organization.

Also, we fix the fact that the docstring was (and I think always has
been) at best confusing and perhaps even inaccurate.
This commit is contained in:
Tim Abbott
2017-11-17 13:43:16 -08:00
parent f17974ab32
commit fb6abe1b1e

View File

@@ -21,7 +21,7 @@ from zerver.lib.subdomains import user_matches_subdomain, get_subdomain
from zerver.lib.users import check_full_name from zerver.lib.users import check_full_name
from zerver.models import UserProfile, Realm, get_user_profile_by_id, \ from zerver.models import UserProfile, Realm, get_user_profile_by_id, \
get_user_profile_by_email, remote_user_to_email, email_to_username, \ get_user_profile_by_email, remote_user_to_email, email_to_username, \
get_realm get_realm, get_user
def pad_method_dict(method_dict): def pad_method_dict(method_dict):
# type: (Dict[Text, bool]) -> Dict[Text, bool] # type: (Dict[Text, bool]) -> Dict[Text, bool]
@@ -85,6 +85,32 @@ def require_email_format_usernames(realm=None):
return False return False
return True return True
def common_get_active_user(email: str, realm: Realm,
return_data: Dict[str, Any]=None) -> Optional[UserProfile]:
try:
user_profile = get_user(email, realm)
except UserProfile.DoesNotExist:
# If the user doesn't have an account in the target realm, we
# check whether they might have an account in another realm,
# and if so, provide a helpful error message via
# `invalid_subdomain`.
try:
user_profile = get_user_profile_by_email(email)
except UserProfile.DoesNotExist:
return None
if return_data is not None:
return_data['invalid_subdomain'] = True
return None
if not user_profile.is_active:
if return_data is not None:
return_data['inactive_user'] = True
return None
if user_profile.realm.deactivated:
if return_data is not None:
return_data['inactive_realm'] = True
return None
return user_profile
def common_get_active_user_by_email(email, return_data=None): def common_get_active_user_by_email(email, return_data=None):
# type: (Text, Optional[Dict[str, Any]]) -> Optional[UserProfile] # type: (Text, Optional[Dict[str, Any]]) -> Optional[UserProfile]
try: try:
@@ -285,23 +311,19 @@ class SocialAuthMixin(ZulipAuthMixin):
class ZulipDummyBackend(ZulipAuthMixin): class ZulipDummyBackend(ZulipAuthMixin):
""" """
Used when we want to log you in but we don't know which backend to use. Used when we want to log you in without checking any
authentication (i.e. new user registration or when otherwise
authentication has already been checked earlier in the process).
""" """
def authenticate(self, username: Optional[Text]=None, realm: Optional[Realm]=None, def authenticate(self, username: Optional[str]=None, realm: Optional[Realm]=None,
use_dummy_backend: bool=False, use_dummy_backend: bool=False,
return_data: Optional[Dict[str, Any]]=None) -> Optional[UserProfile]: return_data: Dict[str, Any]=None) -> Optional[UserProfile]:
assert username is not None
assert realm is not None
if use_dummy_backend: if use_dummy_backend:
user_profile = common_get_active_user_by_email(username) # These are kwargs only for readability; they should never be None
if user_profile is None: assert username is not None
return None assert realm is not None
if not user_matches_subdomain(realm.subdomain, user_profile): return common_get_active_user(username, realm, return_data)
if return_data is not None:
return_data["invalid_subdomain"] = True
return None
return user_profile
return None return None
class EmailAuthBackend(ZulipAuthMixin): class EmailAuthBackend(ZulipAuthMixin):