def _common_authenticate(self, *args, **kwargs): # type: (*Any, **Any) -> Optional[UserProfile] return_data = kwargs.get('return_data', {}) email_address = self.get_email_address(*args, **kwargs) if not email_address: return_data['invalid_email'] = True return None try: user_profile = get_user_profile_by_email(email_address) except UserProfile.DoesNotExist: return_data["valid_attestation"] = True return None if not user_profile.is_active: return_data["inactive_user"] = True return None if user_profile.realm.deactivated: return_data["inactive_realm"] = True return None if not user_matches_subdomain(kwargs.get("realm_subdomain"), user_profile): return_data["invalid_subdomain"] = True return None if not auth_enabled_helper([self.auth_backend_name], user_profile.realm): return_data["auth_backend_disabled"] = True return None return user_profile
def authenticate(self, username=None, password=None, realm_subdomain=None, return_data=None): # type: (Optional[Text], Optional[str], Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile] """ Authenticate a user based on email address as the user name. """ if username is None or password is None: # Return immediately. Otherwise we will look for a SQL row with # NULL username. While that's probably harmless, it's needless # exposure. return None user_profile = common_get_active_user_by_email(username, return_data=return_data) if user_profile is None: return None if not password_auth_enabled(user_profile.realm): if return_data is not None: return_data['password_auth_disabled'] = True return None if not email_auth_enabled(user_profile.realm): if return_data is not None: return_data['email_auth_disabled'] = True return None if user_profile.check_password(password): if not user_matches_subdomain(realm_subdomain, user_profile): if return_data is not None: return_data["invalid_subdomain"] = True return None return user_profile return None
def send_mail(self, context, from_email, to_email): # type: (Dict[str, Any], str, str) -> None """ Currently we don't support accounts in multiple subdomains using a single email address. We override this function so that we do not send a reset link to an email address if the reset attempt is done on the subdomain which does not match user.realm.subdomain. Once we start supporting accounts with the same email in multiple subdomains, we may be able to refactor this function. A second reason we override this function is so that we can send the mail through the functions in zerver.lib.send_email, to match how we send all other mail in the codebase. """ user = get_user_profile_by_email(to_email) attempted_subdomain = get_subdomain(self.request) context['attempted_realm'] = False if not user_matches_subdomain(attempted_subdomain, user): context['attempted_realm'] = get_realm(attempted_subdomain) send_email('zerver/emails/password_reset', to_user_id=user.id, from_name="Zulip Account Security", from_address=FromAddress.NOREPLY, context=context)
def clean_username(self): # type: () -> str email = self.cleaned_data['username'] try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return email if user_profile.realm.deactivated: error_msg = u"""Sorry for the trouble, but %s has been deactivated. Please contact %s to reactivate this group.""" % (user_profile.realm.name, FromAddress.SUPPORT) raise ValidationError(mark_safe(error_msg)) if not user_profile.is_active and not user_profile.is_mirror_dummy: error_msg = ( u"Sorry for the trouble, but your account has been deactivated. " u"Please contact your organization administrator to reactivate it. " u"If you're not sure who that is, try contacting %s.") % ( FromAddress.SUPPORT, ) raise ValidationError(mark_safe(error_msg)) if not user_matches_subdomain(get_subdomain(self.request), user_profile): logging.warning( "User %s attempted to password login to wrong subdomain %s" % (user_profile.email, get_subdomain(self.request))) raise ValidationError(mark_safe(WRONG_SUBDOMAIN_ERROR)) return email
def authenticate(self, google_oauth2_token=None, realm_subdomain=None, return_data=None): # type: (Optional[str], Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile] if return_data is None: return_data = {} try: token_payload = googleapiclient.verify_id_token(google_oauth2_token, settings.GOOGLE_CLIENT_ID) except AppIdentityError: return None if token_payload["email_verified"] in (True, "true"): try: user_profile = get_user_profile_by_email(token_payload["email"]) except UserProfile.DoesNotExist: return_data["valid_attestation"] = True return None if not user_profile.is_active: return_data["inactive_user"] = True return None if user_profile.realm.deactivated: return_data["inactive_realm"] = True return None if not user_matches_subdomain(realm_subdomain, user_profile): return_data["invalid_subdomain"] = True return None if not google_auth_enabled(realm=user_profile.realm): return_data["google_auth_disabled"] = True return None return user_profile else: return_data["valid_attestation"] = False return None
def clean_username(self): # type: () -> str email = self.cleaned_data['username'] try: user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: return email if user_profile.realm.deactivated: error_msg = u"""Sorry for the trouble, but %s has been deactivated. Please contact %s to reactivate this group.""" % ( user_profile.realm.name, FromAddress.SUPPORT) raise ValidationError(mark_safe(error_msg)) if not user_profile.is_active and not user_profile.is_mirror_dummy: error_msg = ( u"Sorry for the trouble, but your account has been deactivated. " u"Please contact your organization administrator to reactivate it. " u"If you're not sure who that is, try contacting %s.") % (FromAddress.SUPPORT,) raise ValidationError(mark_safe(error_msg)) if not user_matches_subdomain(get_subdomain(self.request), user_profile): logging.warning("User %s attempted to password login to wrong subdomain %s" % (user_profile.email, get_subdomain(self.request))) raise ValidationError(mark_safe(WRONG_SUBDOMAIN_ERROR)) return email
def logged_in_and_active(request: HttpRequest) -> bool: if not request.user.is_authenticated: return False if not request.user.is_active: return False if request.user.realm.deactivated: return False return user_matches_subdomain(get_subdomain(request), request.user)
def authenticate(self, remote_user, realm_subdomain=None): # type: (Optional[str], Optional[Text]) -> Optional[UserProfile] if not remote_user: return None email = remote_user_to_email(remote_user) user_profile = common_get_active_user_by_email(email) if user_profile is None: return None if not user_matches_subdomain(realm_subdomain, user_profile): return None if not auth_enabled_helper(["RemoteUser"], user_profile.realm): return None return user_profile
def authenticate(self, username=None, realm_subdomain=None, use_dummy_backend=False, return_data=None): # type: (Optional[Text], Optional[Text], bool, Optional[Dict[str, Any]]) -> Optional[UserProfile] assert username is not None if use_dummy_backend: user_profile = common_get_active_user_by_email(username) if user_profile is None: return None if not user_matches_subdomain(realm_subdomain, user_profile): if return_data is not None: return_data["invalid_subdomain"] = True return None return user_profile return None
def validate_account_and_subdomain(request: HttpRequest, user_profile: UserProfile) -> None: if user_profile.realm.deactivated: raise JsonableError(_("This organization has been deactivated")) if not user_profile.is_active: raise JsonableError(_("Account is deactivated")) # Either the subdomain matches, or we're accessing Tornado from # and to localhost (aka spoofing a request as the user). if (not user_matches_subdomain(get_subdomain(request), user_profile) and not (settings.RUNNING_INSIDE_TORNADO and request.META["SERVER_NAME"] == "127.0.0.1" and request.META["REMOTE_ADDR"] == "127.0.0.1")): logging.warning("User %s (%s) attempted to access API on wrong subdomain (%s)" % ( user_profile.delivery_email, user_profile.realm.subdomain, get_subdomain(request))) raise JsonableError(_("Account is not associated with this subdomain"))
def authenticate(self, username, password, realm_subdomain=None, return_data=None): # type: (Text, str, Optional[Text], Optional[Dict[str, Any]]) -> Optional[UserProfile] try: self._realm = get_realm(realm_subdomain) username = self.django_to_ldap_username(username) user_profile = ZulipLDAPAuthBackendBase.authenticate(self, username=username, password=password) if user_profile is None: return None if not user_matches_subdomain(realm_subdomain, user_profile): return None return user_profile except Realm.DoesNotExist: return None # nocoverage # TODO: this may no longer be possible except ZulipLDAPException: return None # nocoverage # TODO: this may no longer be possible
def validate_account_and_subdomain(request: HttpRequest, user_profile: UserProfile) -> None: if user_profile.realm.deactivated: raise JsonableError(_("This organization has been deactivated")) if not user_profile.is_active: raise JsonableError(_("Account is deactivated")) # Either the subdomain matches, or processing a websockets message # in the message_sender worker (which will have already had the # subdomain validated), or we're accessing Tornado from and to # localhost (aka spoofing a request as the user). if (not user_matches_subdomain(get_subdomain(request), user_profile) and not (request.method == "SOCKET" and request.META['SERVER_NAME'] == "127.0.0.1") and not (settings.RUNNING_INSIDE_TORNADO and request.META["SERVER_NAME"] == "127.0.0.1" and request.META["REMOTE_ADDR"] == "127.0.0.1")): logging.warning("User %s (%s) attempted to access API on wrong subdomain (%s)" % ( user_profile.email, user_profile.realm.subdomain, get_subdomain(request))) raise JsonableError(_("Account is not associated with this subdomain"))
def save( self, domain_override=None, # type: Optional[bool] subject_template_name='registration/password_reset_subject.txt', # type: Text email_template_name='registration/password_reset_email.html', # type: Text use_https=False, # type: bool token_generator=default_token_generator, # type: PasswordResetTokenGenerator from_email=None, # type: Optional[Text] request=None, # type: HttpRequest html_email_template_name=None, # type: Optional[Text] extra_email_context=None # type: Optional[Dict[str, Any]] ): # type: (...) -> None """ If the email address has an account in the target realm, generates a one-use only link for resetting password and sends to the user. We send a different email if an associated account does not exist in the database, or an account does exist, but not in the realm. Note: We ignore protocol and the various email template arguments (those are an artifact of using Django's password reset framework). """ email = self.cleaned_data["email"] subdomain = get_subdomain(request) realm = get_realm(subdomain) if not email_auth_enabled(realm): logging.info( "Password reset attempted for %s even though password auth is disabled." % (email, )) return try: user = get_user_profile_by_email(email) except UserProfile.DoesNotExist: user = None context = { 'email': email, 'realm_uri': realm.uri, 'user': user, } if user is not None and user_matches_subdomain(subdomain, user): token = token_generator.make_token(user) uid = urlsafe_base64_encode(force_bytes(user.id)) endpoint = reverse( 'django.contrib.auth.views.password_reset_confirm', kwargs=dict(uidb64=uid, token=token)) context['no_account_in_realm'] = False context['reset_url'] = "{}{}".format(user.realm.uri, endpoint) send_email('zerver/emails/password_reset', to_user_id=user.id, from_name="Zulip Account Security", from_address=FromAddress.NOREPLY, context=context) else: context['no_account_in_realm'] = True if user is not None: context['account_exists_another_realm'] = True else: context['account_exists_another_realm'] = False send_email('zerver/emails/password_reset', to_email=email, from_name="Zulip Account Security", from_address=FromAddress.NOREPLY, context=context)