def remote_user_sso(request: HttpRequest, mobile_flow_otp: Optional[str]=REQ(default=None)) -> HttpResponse: try: remote_user = request.META["REMOTE_USER"] except KeyError: # TODO: Arguably the JsonableError values here should be # full-page HTML configuration errors instead. raise JsonableError(_("No REMOTE_USER set.")) # 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(remote_user_to_email(remote_user)) # Here we support the mobile flow for REMOTE_USER_BACKEND; we # validate the data format and then pass it through to # login_or_register_remote_user if appropriate. if mobile_flow_otp is not None: if not is_valid_otp(mobile_flow_otp): raise JsonableError(_("Invalid OTP")) subdomain = get_subdomain(request) realm = get_realm(subdomain) # Since RemoteUserBackend will return None if Realm is None, we # don't need to check whether `get_realm` returned None. user_profile = authenticate(remote_user=remote_user, realm=realm) return login_or_register_remote_user(request, remote_user, user_profile, mobile_flow_otp=mobile_flow_otp)
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_fetch_api_key(request, username=REQ(), password=REQ()): # type: (HttpRequest, str, str) -> HttpResponse return_data = {} # type: Dict[str, bool] if username == "google-oauth2-token": user_profile = authenticate(google_oauth2_token=password, realm_subdomain=get_subdomain(request), return_data=return_data) else: if not ldap_auth_enabled(realm=get_realm_from_request(request)): # In case we don't authenticate against LDAP, check for a valid # email. LDAP backend can authenticate against a non-email. validate_login_email(username) user_profile = authenticate(username=username, password=password, realm_subdomain=get_subdomain(request), return_data=return_data) if return_data.get("inactive_user"): return json_error(_("Your account has been disabled."), data={"reason": "user disable"}, status=403) if return_data.get("inactive_realm"): return json_error(_("Your realm has been deactivated."), data={"reason": "realm deactivated"}, status=403) if return_data.get("password_auth_disabled"): return json_error(_("Password auth is disabled in your team."), data={"reason": "password auth disabled"}, status=403) if user_profile is None: if return_data.get("valid_attestation"): # We can leak that the user is unregistered iff they present a valid authentication string for the user. return json_error(_("This user is not registered; do so from a browser."), data={"reason": "unregistered"}, status=403) return json_error(_("Your username or password is incorrect."), data={"reason": "incorrect_creds"}, status=403) return json_success({"api_key": user_profile.api_key, "email": user_profile.email})
def api_fetch_api_key(request: HttpRequest, username: str=REQ(), password: str=REQ()) -> HttpResponse: return_data = {} # type: Dict[str, bool] subdomain = get_subdomain(request) realm = get_realm(subdomain) if username == "google-oauth2-token": # This code path is auth for the legacy Android app user_profile = authenticate(google_oauth2_token=password, realm=realm, return_data=return_data) else: if not ldap_auth_enabled(realm=get_realm_from_request(request)): # In case we don't authenticate against LDAP, check for a valid # email. LDAP backend can authenticate against a non-email. validate_login_email(username) user_profile = authenticate(username=username, password=password, realm=realm, return_data=return_data) if return_data.get("inactive_user"): return json_error(_("Your account has been disabled."), data={"reason": "user disable"}, status=403) if return_data.get("inactive_realm"): return json_error(_("This organization has been deactivated."), data={"reason": "realm deactivated"}, status=403) if return_data.get("password_auth_disabled"): return json_error(_("Password auth is disabled in your team."), data={"reason": "password auth disabled"}, status=403) if user_profile is None: if return_data.get("valid_attestation"): # We can leak that the user is unregistered iff # they present a valid authentication string for the user. return json_error(_("This user is not registered; do so from a browser."), data={"reason": "unregistered"}, status=403) return json_error(_("Your username or password is incorrect."), data={"reason": "incorrect_creds"}, status=403) # 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 api_key = get_api_key(user_profile) return json_success({"api_key": api_key, "email": user_profile.email})
def remote_user_sso(request): # type: (HttpRequest) -> HttpResponse try: remote_user = request.META["REMOTE_USER"] except KeyError: raise JsonableError(_("No REMOTE_USER set.")) # 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(remote_user_to_email(remote_user)) user_profile = authenticate(remote_user=remote_user, realm_subdomain=get_subdomain(request)) return login_or_register_remote_user(request, remote_user, user_profile)
def remote_user_sso(request: HttpRequest) -> HttpResponse: try: remote_user = request.META["REMOTE_USER"] except KeyError: raise JsonableError(_("No REMOTE_USER set.")) # 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(remote_user_to_email(remote_user)) subdomain = get_subdomain(request) realm = get_realm(subdomain) # Since RemoteUserBackend will return None if Realm is None, we # don't need to check whether `get_realm` returned None. user_profile = authenticate(remote_user=remote_user, realm=realm) return login_or_register_remote_user(request, remote_user, user_profile)
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: 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_fetch_api_key( request: HttpRequest, username: str = REQ(), password: str = REQ() ) -> HttpResponse: return_data: Dict[str, bool] = {} realm = get_realm_from_request(request) if realm is None: raise InvalidSubdomainError() if not ldap_auth_enabled(realm=realm): # In case we don't authenticate against LDAP, check for a valid # email. LDAP backend can authenticate against a non-email. validate_login_email(username) user_profile = authenticate( request=request, username=username, password=password, realm=realm, return_data=return_data ) if return_data.get("inactive_user"): raise UserDeactivatedError() if return_data.get("inactive_realm"): raise RealmDeactivatedError() if return_data.get("password_auth_disabled"): raise PasswordAuthDisabledError() if return_data.get("password_reset_needed"): raise PasswordResetRequiredError() if user_profile is None: raise AuthenticationFailedError() # 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._requestor_for_logs = user_profile.format_requestor_for_logs() api_key = get_api_key(user_profile) return json_success({"api_key": api_key, "email": user_profile.delivery_email})
def remote_user_sso(request: HttpRequest, mobile_flow_otp: Optional[str]=REQ(default=None)) -> HttpResponse: subdomain = get_subdomain(request) try: realm = get_realm(subdomain) # type: Optional[Realm] except Realm.DoesNotExist: realm = None if not auth_enabled_helper([ZulipRemoteUserBackend.auth_backend_name], realm): return redirect_to_config_error("remoteuser/backend_disabled") try: remote_user = request.META["REMOTE_USER"] except KeyError: return redirect_to_config_error("remoteuser/remote_user_header_missing") # 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(remote_user_to_email(remote_user)) # Here we support the mobile flow for REMOTE_USER_BACKEND; we # validate the data format and then pass it through to # login_or_register_remote_user if appropriate. if mobile_flow_otp is not None: if not is_valid_otp(mobile_flow_otp): raise JsonableError(_("Invalid OTP")) subdomain = get_subdomain(request) if realm is None: user_profile = None else: user_profile = authenticate(remote_user=remote_user, realm=realm) redirect_to = request.GET.get('next', '') return login_or_register_remote_user(request, remote_user, user_profile, mobile_flow_otp=mobile_flow_otp, redirect_to=redirect_to)
def api_fetch_api_key(request: HttpRequest, username: str=REQ(), password: str=REQ()) -> HttpResponse: return_data = {} # type: Dict[str, bool] subdomain = get_subdomain(request) realm = get_realm(subdomain) if not ldap_auth_enabled(realm=get_realm_from_request(request)): # In case we don't authenticate against LDAP, check for a valid # email. LDAP backend can authenticate against a non-email. validate_login_email(username) user_profile = authenticate(request=request, username=username, password=password, realm=realm, return_data=return_data) if return_data.get("inactive_user"): return json_error(_("Your account has been disabled."), data={"reason": "user disable"}, status=403) if return_data.get("inactive_realm"): return json_error(_("This organization has been deactivated."), data={"reason": "realm deactivated"}, status=403) if return_data.get("password_auth_disabled"): return json_error(_("Password auth is disabled in your team."), data={"reason": "password auth disabled"}, status=403) if user_profile is None: return json_error(_("Your username or password is incorrect."), data={"reason": "incorrect_creds"}, status=403) # 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.delivery_email 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: 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({ "api_key": api_key, "email": user_profile.delivery_email })
def remote_user_sso(request: HttpRequest, mobile_flow_otp: Optional[str]=REQ(default=None)) -> HttpResponse: try: remote_user = request.META["REMOTE_USER"] except KeyError: # TODO: Arguably the JsonableError values here should be # full-page HTML configuration errors instead. raise JsonableError(_("No REMOTE_USER set.")) # 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(remote_user_to_email(remote_user)) # Here we support the mobile flow for REMOTE_USER_BACKEND; we # validate the data format and then pass it through to # login_or_register_remote_user if appropriate. if mobile_flow_otp is not None: if not is_valid_otp(mobile_flow_otp): raise JsonableError(_("Invalid OTP")) subdomain = get_subdomain(request) try: realm = get_realm(subdomain) # type: Optional[Realm] except Realm.DoesNotExist: realm = None # Since RemoteUserBackend will return None if Realm is None, we # don't need to check whether `realm` is None. return_data = {} # type: Dict[str, Any] user_profile = authenticate(remote_user=remote_user, realm=realm, return_data=return_data) redirect_to = request.GET.get('next', '') return login_or_register_remote_user(request, remote_user, user_profile, invalid_subdomain = bool(return_data.get("invalid_subdomain")), mobile_flow_otp=mobile_flow_otp, redirect_to=redirect_to)