def api_dev_fetch_api_key(request: HttpRequest, username: str = REQ()) -> HttpResponse: """This function allows logging in without a password on the Zulip mobile apps when connecting to a Zulip development environment. It requires DevAuthBackend to be included in settings.AUTHENTICATION_BACKENDS. """ check_dev_auth_backend() # Django invokes authenticate methods by matching arguments, and this # authentication flow will not invoke LDAP authentication because of # this condition of Django so no need to check if LDAP backend is # enabled. validate_login_email(username) realm = get_realm_from_request(request) if realm is None: raise InvalidSubdomainError() return_data: Dict[str, bool] = {} user_profile = authenticate(dev_auth_username=username, realm=realm, return_data=return_data) if return_data.get("inactive_realm"): raise RealmDeactivatedError() if return_data.get("inactive_user"): raise UserDeactivatedError() if return_data.get("invalid_subdomain"): raise InvalidSubdomainError() if user_profile is None: # Since we're not actually checking passwords, this condition # is when one's attempting to send an email address that # doesn't have an account, i.e. it's definitely invalid username. raise AuthenticationFailedError() assert user_profile is not None do_login(request, user_profile) api_key = get_api_key(user_profile) return json_success(request, data={"api_key": api_key, "email": user_profile.delivery_email})
def dev_direct_login( request: HttpRequest, next: str = REQ(default="/"), ) -> HttpResponse: # This function allows logging in without a password and should only be called # in development environments. It may be called if the DevAuthBackend is included # in settings.AUTHENTICATION_BACKENDS if (not dev_auth_enabled()) or settings.PRODUCTION: # This check is probably not required, since authenticate would fail without # an enabled DevAuthBackend. return config_error(request, "dev") subdomain = get_subdomain(request) realm = get_realm(subdomain) if request.POST.get("prefers_web_public_view") == "Anonymous login": redirect_to = get_safe_redirect_to(next, realm.uri) return HttpResponseRedirect(redirect_to) email = request.POST["direct_email"] user_profile = authenticate(dev_auth_username=email, realm=realm) if user_profile is None: return config_error(request, "dev") assert isinstance(user_profile, UserProfile) do_login(request, user_profile) redirect_to = get_safe_redirect_to(next, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def api_dev_fetch_api_key(request: HttpRequest, username: str=REQ()) -> HttpResponse: """This function allows logging in without a password on the Zulip mobile apps when connecting to a Zulip development environment. It requires DevAuthBackend to be included in settings.AUTHENTICATION_BACKENDS. """ if not dev_auth_enabled() or settings.PRODUCTION: return json_error(_("Dev environment not enabled.")) # Django invokes authenticate methods by matching arguments, and this # authentication flow will not invoke LDAP authentication because of # this condition of Django so no need to check if LDAP backend is # enabled. validate_login_email(username) subdomain = get_subdomain(request) realm = get_realm(subdomain) return_data = {} # type: Dict[str, bool] user_profile = authenticate(dev_auth_username=username, realm=realm, return_data=return_data) if return_data.get("inactive_realm"): return json_error(_("Your realm has been deactivated."), data={"reason": "realm deactivated"}, status=403) if return_data.get("inactive_user"): return json_error(_("Your account has been disabled."), data={"reason": "user disable"}, status=403) if user_profile is None: return json_error(_("This user is not registered."), data={"reason": "unregistered"}, status=403) do_login(request, user_profile) return json_success({"api_key": user_profile.api_key, "email": user_profile.email})
def api_dev_fetch_api_key(request: HttpRequest, username: str = REQ()) -> HttpResponse: """This function allows logging in without a password on the Zulip mobile apps when connecting to a Zulip development environment. It requires DevAuthBackend to be included in settings.AUTHENTICATION_BACKENDS. """ check_dev_auth_backend() # Django invokes authenticate methods by matching arguments, and this # authentication flow will not invoke LDAP authentication because of # this condition of Django so no need to check if LDAP backend is # enabled. validate_login_email(username) realm = get_realm_from_request(request) if realm is None: return json_error(_("Invalid subdomain")) return_data: Dict[str, bool] = {} user_profile = authenticate(dev_auth_username=username, realm=realm, return_data=return_data) if return_data.get("inactive_realm"): return json_error( _("This organization has been deactivated."), data={"reason": "realm deactivated"}, status=403, ) if return_data.get("inactive_user"): return json_error( _("Your account has been disabled."), data={"reason": "user disable"}, status=403 ) if user_profile is None: return json_error( _("This user is not registered."), data={"reason": "unregistered"}, status=403 ) do_login(request, user_profile) api_key = get_api_key(user_profile) return json_success({"api_key": api_key, "email": user_profile.delivery_email})
def api_dev_fetch_api_key(request, username=REQ()): # type: (HttpRequest, str) -> HttpResponse """This function allows logging in without a password on the Zulip mobile apps when connecting to a Zulip development environment. It requires DevAuthBackend to be included in settings.AUTHENTICATION_BACKENDS. """ if not dev_auth_enabled() or settings.PRODUCTION: return json_error(_("Dev environment not enabled.")) # Django invokes authenticate methods by matching arguments, and this # authentication flow will not invoke LDAP authentication because of # this condition of Django so no need to check if LDAP backend is # enabled. validate_login_email(username) return_data = {} # type: Dict[str, bool] user_profile = authenticate(username=username, realm_subdomain=get_subdomain(request), return_data=return_data) if return_data.get("inactive_realm"): return json_error(_("Your realm has been deactivated."), data={"reason": "realm deactivated"}, status=403) if return_data.get("inactive_user"): return json_error(_("Your account has been disabled."), data={"reason": "user disable"}, status=403) if user_profile is None: return json_error(_("This user is not registered."), data={"reason": "unregistered"}, status=403) do_login(request, user_profile) return json_success({"api_key": user_profile.api_key, "email": user_profile.email})
def login_and_go_to_home(request, user_profile): # type: (HttpRequest, UserProfile) -> HttpResponse # Mark the user as having been just created, so no "new login" email is sent user_profile.just_registered = True do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri + reverse('zerver.views.home.home'))
def login_or_register_remote_user( request: HttpRequest, remote_username: Optional[str], user_profile: Optional[UserProfile], full_name: str = '', invalid_subdomain: bool = False, mobile_flow_otp: Optional[str] = None, is_signup: bool = False, redirect_to: str = '', multiuse_object_key: str = '') -> HttpResponse: email = remote_user_to_email(remote_username) if user_profile is None or user_profile.is_mirror_dummy: # We have verified the user controls an email address, but # there's no associated Zulip user account. Consider sending # the request to registration. return maybe_send_to_registration( request, email, full_name, password_required=False, is_signup=is_signup, multiuse_object_key=multiuse_object_key) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. api_key = get_api_key(user_profile) params = { 'otp_encrypted_api_key': otp_encrypt_api_key(api_key, mobile_flow_otp), 'email': email, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode( params) # Maybe sending 'user_logged_in' signal is the better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) # Not doing this only because over here we don't add the user information # in the session. If the signal receiver assumes that we do then that # would cause problems. email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def login_or_register_remote_user(request, remote_username, user_profile, full_name='', invalid_subdomain=False, mobile_flow_otp=None, is_signup=False): # type: (HttpRequest, Optional[Text], Optional[UserProfile], Text, bool, Optional[str], bool) -> HttpResponse if invalid_subdomain: # Show login page with an error message return redirect_to_subdomain_login_url() if user_profile is None or user_profile.is_mirror_dummy: # Since execution has reached here, we have verified the user # controls an email address (remote_username) but there's no # associated Zulip user account. if is_signup: # If they're trying to sign up, send them over to the PreregistrationUser flow. return maybe_send_to_registration(request, remote_user_to_email(remote_username), full_name, password_required=False) # Otherwise, we send them to a special page that asks if they # want to register or provided the wrong email and want to go back. try: validate_email(remote_username) invalid_email = False except ValidationError: # If email address is invalid, we can't send the user # PreregistrationUser flow. invalid_email = True context = {'full_name': full_name, 'email': remote_username, 'invalid_email': invalid_email} return render(request, 'zerver/confirm_continue_registration.html', context=context) if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. params = { 'otp_encrypted_api_key': otp_encrypt_api_key(user_profile, mobile_flow_otp), 'email': remote_username, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode(params) # Maybe sending 'user_logged_in' signal is the better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) # Not doing this only because over here we don't add the user information # in the session. If the signal receiver assumes that we do then that # would cause problems. email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri)
def login_or_register_remote_user(request: HttpRequest, remote_username: Optional[Text], user_profile: Optional[UserProfile], full_name: Text='', invalid_subdomain: bool=False, mobile_flow_otp: Optional[str]=None, is_signup: bool=False) -> HttpResponse: if user_profile is None or user_profile.is_mirror_dummy: # Since execution has reached here, we have verified the user # controls an email address (remote_username) but there's no # associated Zulip user account. if is_signup: # If they're trying to sign up, send them over to the PreregistrationUser flow. return maybe_send_to_registration(request, remote_user_to_email(remote_username), full_name, password_required=False) # Otherwise, we send them to a special page that asks if they # want to register or provided the wrong email and want to go back. try: validate_email(remote_username) invalid_email = False except ValidationError: # If email address is invalid, we can't send the user # PreregistrationUser flow. invalid_email = True context = {'full_name': full_name, 'email': remote_username, 'invalid_email': invalid_email} return render(request, 'zerver/confirm_continue_registration.html', context=context) if invalid_subdomain: # Show login page with an error message return redirect_to_subdomain_login_url() if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. params = { 'otp_encrypted_api_key': otp_encrypt_api_key(user_profile, mobile_flow_otp), 'email': remote_username, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode(params) # Maybe sending 'user_logged_in' signal is the better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) # Not doing this only because over here we don't add the user information # in the session. If the signal receiver assumes that we do then that # would cause problems. email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri)
def bypass_login(request, chat_id=REQ(), chat_token=REQ()): return_data = {} # type: Dict[str, bool] user_profile = authenticate(username=chat_id, password=chat_token, realm_subdomain=get_subdomain(request), return_data=return_data) do_login(request, user_profile) if settings.REALMS_HAVE_SUBDOMAINS and user_profile.realm.subdomain is not None: return HttpResponseRedirect(user_profile.realm.uri) return HttpResponseRedirect( "%s%s" % (settings.EXTERNAL_URI_SCHEME, request.get_host()))
def login_and_go_to_home(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: mobile_flow_otp = get_expirable_session_var(request.session, 'registration_mobile_flow_otp', delete=True) desktop_flow_otp = get_expirable_session_var(request.session, 'registration_desktop_flow_otp', delete=True) if mobile_flow_otp is not None: return finish_mobile_flow(request, user_profile, mobile_flow_otp) elif desktop_flow_otp is not None: return finish_desktop_flow(request, user_profile, desktop_flow_otp) do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri + reverse('zerver.views.home.home'))
def login_or_register_remote_user( request: HttpRequest, remote_username: str, user_profile: Optional[UserProfile], full_name: str = '', mobile_flow_otp: Optional[str] = None, desktop_flow_otp: Optional[str] = None, realm: Optional[Realm] = None, is_signup: bool = False, redirect_to: str = '', multiuse_object_key: str = '', full_name_validated: bool = False) -> HttpResponse: """Given a successful authentication showing the user controls given email address (remote_username) and potentially a UserProfile object (if the user already has a Zulip account), redirect the browser to the appropriate place: * The logged-in app if the user already has a Zulip account and is trying to login, potentially to an initial narrow or page that had been saved in the `redirect_to` parameter. * The registration form if is_signup was set (i.e. the user is trying to create a Zulip account) * A special `confirm_continue_registration.html` "do you want to register or try another account" if the user doesn't have a Zulip account but is_signup is False (i.e. the user tried to login and then did social authentication selecting an email address that does not have a Zulip account in this organization). * A zulip:// URL to send control back to the mobile or desktop apps if they are doing authentication using the mobile_flow_otp or desktop_flow_otp flow. """ if user_profile is None or user_profile.is_mirror_dummy: return register_remote_user(request, remote_username, full_name, is_signup=is_signup, multiuse_object_key=multiuse_object_key, full_name_validated=full_name_validated) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. if mobile_flow_otp is not None: return finish_mobile_flow(request, user_profile, mobile_flow_otp) elif desktop_flow_otp is not None: assert realm is not None return finish_desktop_flow(request, user_profile, realm, desktop_flow_otp) do_login(request, user_profile) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def dev_direct_login(request, **kwargs): # type: (HttpRequest, **Any) -> HttpResponse # This function allows logging in without a password and should only be called in development environments. # It may be called if the DevAuthBackend is included in settings.AUTHENTICATION_BACKENDS if (not dev_auth_enabled()) or settings.PRODUCTION: # This check is probably not required, since authenticate would fail without an enabled DevAuthBackend. raise Exception('Direct login not supported.') email = request.POST['direct_email'] user_profile = authenticate(username=email, realm_subdomain=get_subdomain(request)) if user_profile is None: raise Exception("User cannot login") do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri)
def login_and_go_to_home(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: mobile_flow_otp = get_expirable_session_var(request.session, 'registration_mobile_flow_otp', delete=True) desktop_flow_otp = get_expirable_session_var(request.session, 'registration_desktop_flow_otp', delete=True) if mobile_flow_otp is not None: return finish_mobile_flow(request, user_profile, mobile_flow_otp) elif desktop_flow_otp is not None: return finish_desktop_flow(request, user_profile, desktop_flow_otp) do_login(request, user_profile) # Using 'mark_sanitized' to work around false positive where Pysa thinks # that 'user_profile' is user-controlled return HttpResponseRedirect(mark_sanitized(user_profile.realm.uri) + reverse('home'))
def login_or_register_remote_user(request: HttpRequest, result: ExternalAuthResult) -> HttpResponse: """Given a successful authentication showing the user controls given email address (email) and potentially a UserProfile object (if the user already has a Zulip account), redirect the browser to the appropriate place: * The logged-in app if the user already has a Zulip account and is trying to login, potentially to an initial narrow or page that had been saved in the `redirect_to` parameter. * The registration form if is_signup was set (i.e. the user is trying to create a Zulip account) * A special `confirm_continue_registration.html` "do you want to register or try another account" if the user doesn't have a Zulip account but is_signup is False (i.e. the user tried to login and then did social authentication selecting an email address that does not have a Zulip account in this organization). * A zulip:// URL to send control back to the mobile or desktop apps if they are doing authentication using the mobile_flow_otp or desktop_flow_otp flow. """ user_profile = result.user_profile if user_profile is None or user_profile.is_mirror_dummy: return register_remote_user(request, result) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. is_realm_creation = result.data_dict.get('is_realm_creation') mobile_flow_otp = result.data_dict.get('mobile_flow_otp') desktop_flow_otp = result.data_dict.get('desktop_flow_otp') if mobile_flow_otp is not None: return finish_mobile_flow(request, user_profile, mobile_flow_otp) elif desktop_flow_otp is not None: return finish_desktop_flow(request, user_profile, desktop_flow_otp) do_login(request, user_profile) redirect_to = result.data_dict.get('redirect_to', '') if is_realm_creation is not None and settings.FREE_TRIAL_DAYS not in [ None, 0 ]: redirect_to = "{}?onboarding=true".format( reverse('corporate.views.initial_upgrade')) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) logging.error(user_profile.realm.uri) # return HttpResponseRedirect(redirect_to) return redirect_to_upbook_api()
def dev_direct_login(request: HttpRequest, **kwargs: Any) -> HttpResponse: # This function allows logging in without a password and should only be called # in development environments. It may be called if the DevAuthBackend is included # in settings.AUTHENTICATION_BACKENDS if (not dev_auth_enabled()) or settings.PRODUCTION: # This check is probably not required, since authenticate would fail without # an enabled DevAuthBackend. return HttpResponseRedirect(reverse('dev_not_supported')) email = request.POST['direct_email'] subdomain = get_subdomain(request) realm = get_realm(subdomain) user_profile = authenticate(dev_auth_username=email, realm=realm) if user_profile is None: return HttpResponseRedirect(reverse('dev_not_supported')) do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri)
def login_or_register_remote_user(request: HttpRequest, remote_username: Optional[str], user_profile: Optional[UserProfile], full_name: str='', invalid_subdomain: bool=False, mobile_flow_otp: Optional[str]=None, is_signup: bool=False, redirect_to: str='') -> HttpResponse: email = remote_user_to_email(remote_username) if user_profile is None or user_profile.is_mirror_dummy: # We have verified the user controls an email address, but # there's no associated Zulip user account. Consider sending # the request to registration. return maybe_send_to_registration(request, email, full_name, password_required=False, is_signup=is_signup) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. api_key = get_api_key(user_profile) params = { 'otp_encrypted_api_key': otp_encrypt_api_key(api_key, mobile_flow_otp), 'email': email, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode(params) # Maybe sending 'user_logged_in' signal is the better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) # Not doing this only because over here we don't add the user information # in the session. If the signal receiver assumes that we do then that # would cause problems. email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def login_and_go_to_home(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: do_login(request, user_profile) return HttpResponseRedirect(user_profile.realm.uri + reverse('zerver.views.home.home'))
def login_or_register_remote_user(request: HttpRequest, remote_username: Optional[str], user_profile: Optional[UserProfile], full_name: str='', invalid_subdomain: bool=False, mobile_flow_otp: Optional[str]=None, is_signup: bool=False, redirect_to: str='', multiuse_object_key: str='') -> HttpResponse: """Given a successful authentication showing the user controls given email address (remote_username) and potentially a UserProfile object (if the user already has a Zulip account), redirect the browser to the appropriate place: * The logged-in app if the user already has a Zulip account and is trying to login, potentially to an initial narrow or page that had been saved in the `redirect_to` parameter. * The registration form if is_signup was set (i.e. the user is trying to create a Zulip account) * A special `confirm_continue_registration.html` "do you want to register or try another account" if the user doesn't have a Zulip account but is_signup is False (i.e. the user tried to login and then did social authentication selecting an email address that does not have a Zulip account in this organization). * A zulip:// URL to send control back to the mobile apps if they are doing authentication using the mobile_flow_otp flow. """ email = remote_user_to_email(remote_username) if user_profile is None or user_profile.is_mirror_dummy: # We have verified the user controls an email address, but # there's no associated Zulip user account. Consider sending # the request to registration. return maybe_send_to_registration(request, email, full_name, password_required=False, is_signup=is_signup, multiuse_object_key=multiuse_object_key) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. api_key = get_api_key(user_profile) params = { 'otp_encrypted_api_key': otp_encrypt_api_key(api_key, mobile_flow_otp), 'email': email, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode(params) # Since we are returning an API key instead of going through # the Django login() function (which creates a browser # session, etc.), the "new login" signal handler (which # triggers an email notification new logins) will not run # automatically. So we call it manually here. # # Arguably, sending a fake 'user_logged_in' signal would be a better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def login_or_register_remote_user( request: HttpRequest, remote_username: str, user_profile: Optional[UserProfile], full_name: str = '', mobile_flow_otp: Optional[str] = None, is_signup: bool = False, redirect_to: str = '', multiuse_object_key: str = '') -> HttpResponse: """Given a successful authentication showing the user controls given email address (remote_username) and potentially a UserProfile object (if the user already has a Zulip account), redirect the browser to the appropriate place: * The logged-in app if the user already has a Zulip account and is trying to login, potentially to an initial narrow or page that had been saved in the `redirect_to` parameter. * The registration form if is_signup was set (i.e. the user is trying to create a Zulip account) * A special `confirm_continue_registration.html` "do you want to register or try another account" if the user doesn't have a Zulip account but is_signup is False (i.e. the user tried to login and then did social authentication selecting an email address that does not have a Zulip account in this organization). * A zulip:// URL to send control back to the mobile apps if they are doing authentication using the mobile_flow_otp flow. """ email = remote_user_to_email(remote_username) if user_profile is None or user_profile.is_mirror_dummy: # We have verified the user controls an email address, but # there's no associated Zulip user account. Consider sending # the request to registration. return maybe_send_to_registration( request, email, full_name, password_required=False, is_signup=is_signup, multiuse_object_key=multiuse_object_key) # Otherwise, the user has successfully authenticated to an # account, and we need to do the right thing depending whether # or not they're using the mobile OTP flow or want a browser session. if mobile_flow_otp is not None: # For the mobile Oauth flow, we send the API key and other # necessary details in a redirect to a zulip:// URI scheme. api_key = get_api_key(user_profile) params = { 'otp_encrypted_api_key': otp_encrypt_api_key(api_key, mobile_flow_otp), 'email': email, 'realm': user_profile.realm.uri, } # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs response = HttpResponse(status=302) response['Location'] = 'zulip://login?' + urllib.parse.urlencode( params) # Since we are returning an API key instead of going through # the Django login() function (which creates a browser # session, etc.), the "new login" signal handler (which # triggers an email notification new logins) will not run # automatically. So we call it manually here. # # Arguably, sending a fake 'user_logged_in' signal would be a better approach: # user_logged_in.send(sender=user_profile.__class__, request=request, user=user_profile) email_on_new_login(sender=user_profile.__class__, request=request, user=user_profile) # Mark this request as having a logged-in user for our server logs. process_client(request, user_profile) request._email = user_profile.email return response do_login(request, user_profile) redirect_to = get_safe_redirect_to(redirect_to, user_profile.realm.uri) return HttpResponseRedirect(redirect_to)
def accounts_register(request): # type: (HttpRequest) -> HttpResponse key = request.POST['key'] confirmation = Confirmation.objects.get(confirmation_key=key) prereg_user = confirmation.content_object email = prereg_user.email realm_creation = prereg_user.realm_creation password_required = prereg_user.password_required validators.validate_email(email) if prereg_user.referred_by: # If someone invited you, you are joining their realm regardless # of your e-mail address. realm = prereg_user.referred_by.realm elif realm_creation: # For creating a new realm, there is no existing realm or domain realm = None else: realm = get_realm(get_subdomain(request)) if realm and not email_allowed_for_realm(email, realm): return render(request, "zerver/closed_realm.html", context={"closed_domain_name": realm.name}) if realm and realm.deactivated: # The user is trying to register for a deactivated realm. Advise them to # contact support. return redirect_to_deactivation_notice() try: validate_email_for_realm(realm, email) except ValidationError: return HttpResponseRedirect( reverse('django.contrib.auth.views.login') + '?email=' + urllib.parse.quote_plus(email)) name_validated = False full_name = None if request.POST.get('from_confirmation'): try: del request.session['authenticated_full_name'] except KeyError: pass if realm is not None and realm.is_zephyr_mirror_realm: # For MIT users, we can get an authoritative name from Hesiod. # Technically we should check that this is actually an MIT # realm, but we can cross that bridge if we ever get a non-MIT # zephyr mirroring realm. hesiod_name = compute_mit_user_fullname(email) form = RegistrationForm(initial={ 'full_name': hesiod_name if "@" not in hesiod_name else "" }, realm_creation=realm_creation) name_validated = True elif settings.POPULATE_PROFILE_VIA_LDAP: for backend in get_backends(): if isinstance(backend, LDAPBackend): ldap_attrs = _LDAPUser( backend, backend.django_to_ldap_username(email)).attrs try: ldap_full_name = ldap_attrs[ settings.AUTH_LDAP_USER_ATTR_MAP['full_name']][0] request.session[ 'authenticated_full_name'] = ldap_full_name name_validated = True # We don't use initial= here, because if the form is # complete (that is, no additional fields need to be # filled out by the user) we want the form to validate, # so they can be directly registered without having to # go through this interstitial. form = RegistrationForm({'full_name': ldap_full_name}, realm_creation=realm_creation) # FIXME: This will result in the user getting # validation errors if they have to enter a password. # Not relevant for ONLY_SSO, though. break except TypeError: # Let the user fill out a name and/or try another backend form = RegistrationForm(realm_creation=realm_creation) elif 'full_name' in request.POST: form = RegistrationForm( initial={'full_name': request.POST.get('full_name')}, realm_creation=realm_creation) else: form = RegistrationForm(realm_creation=realm_creation) else: postdata = request.POST.copy() if name_changes_disabled(realm): # If we populate profile information via LDAP and we have a # verified name from you on file, use that. Otherwise, fall # back to the full name in the request. try: postdata.update( {'full_name': request.session['authenticated_full_name']}) name_validated = True except KeyError: pass form = RegistrationForm(postdata, realm_creation=realm_creation) if not (password_auth_enabled(realm) and password_required): form['password'].field.required = False if form.is_valid(): if password_auth_enabled(realm): password = form.cleaned_data['password'] else: # SSO users don't need no passwords password = None if realm_creation: string_id = form.cleaned_data['realm_subdomain'] realm_name = form.cleaned_data['realm_name'] realm = do_create_realm(string_id, realm_name) setup_initial_streams(realm) assert (realm is not None) full_name = form.cleaned_data['full_name'] short_name = email_to_username(email) timezone = u"" if 'timezone' in request.POST and request.POST[ 'timezone'] in get_all_timezones(): timezone = request.POST['timezone'] try: existing_user_profile = get_user_profile_by_email(email) except UserProfile.DoesNotExist: existing_user_profile = None if existing_user_profile is not None and existing_user_profile.is_mirror_dummy: user_profile = existing_user_profile do_activate_user(user_profile) do_change_password(user_profile, password) do_change_full_name(user_profile, full_name, user_profile) do_set_user_display_setting(user_profile, 'timezone', timezone) else: user_profile = do_create_user( email, password, realm, full_name, short_name, prereg_user=prereg_user, is_realm_admin=realm_creation, tos_version=settings.TOS_VERSION, timezone=timezone, newsletter_data={"IP": request.META['REMOTE_ADDR']}) send_initial_pms(user_profile) if realm_creation: setup_initial_private_stream(user_profile) send_initial_realm_messages(realm) if realm_creation: # Because for realm creation, registration happens on the # root domain, we need to log them into the subdomain for # their new realm. return redirect_and_log_into_subdomain(realm, full_name, email) # This dummy_backend check below confirms the user is # authenticating to the correct subdomain. return_data = {} # type: Dict[str, bool] auth_result = authenticate(username=user_profile.email, realm_subdomain=realm.subdomain, return_data=return_data, use_dummy_backend=True) if return_data.get('invalid_subdomain'): # By construction, this should never happen. logging.error("Subdomain mismatch in registration %s: %s" % ( realm.subdomain, user_profile.email, )) return redirect('/') # Mark the user as having been just created, so no login email is sent auth_result.just_registered = True do_login(request, auth_result) return HttpResponseRedirect(realm.uri + reverse('zerver.views.home.home')) return render( request, 'zerver/register.html', context={ 'form': form, 'email': email, 'key': key, 'full_name': request.session.get('authenticated_full_name', None), 'lock_name': name_validated and name_changes_disabled(realm), # password_auth_enabled is normally set via our context processor, # but for the registration form, there is no logged in user yet, so # we have to set it here. 'creating_new_team': realm_creation, 'password_required': password_auth_enabled(realm) and password_required, 'password_auth_enabled': password_auth_enabled(realm), 'MAX_REALM_NAME_LENGTH': str(Realm.MAX_REALM_NAME_LENGTH), 'MAX_NAME_LENGTH': str(UserProfile.MAX_NAME_LENGTH), 'MAX_PASSWORD_LENGTH': str(form.MAX_PASSWORD_LENGTH), 'MAX_REALM_SUBDOMAIN_LENGTH': str(Realm.MAX_REALM_SUBDOMAIN_LENGTH) })