Beispiel #1
0
    def authenticate(self, remote_user):
        # type: (str) -> Optional[UserProfile]
        if not remote_user:
            return None

        email = remote_user_to_email(remote_user)
        return common_get_active_user_by_email(email)
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
    def authenticate(self, remote_user: Optional[str], realm: Optional[Realm]=None,
                     return_data: Optional[Dict[str, Any]]=None) -> Optional[UserProfile]:
        assert remote_user is not None
        if realm is None:
            return None
        if not auth_enabled_helper(["RemoteUser"], realm):
            return None

        email = remote_user_to_email(remote_user)
        return common_get_active_user(email, realm, return_data=return_data)
Beispiel #5
0
    def authenticate(self, remote_user, realm_subdomain=None):
        # type: (str, Optional[text_type]) -> Optional[UserProfile]
        if not remote_user:
            return None

        email = remote_user_to_email(remote_user)
        user = common_get_active_user_by_email(email)
        if user is not None and check_subdomain(realm_subdomain, user.realm.subdomain):
            return user
        return None
Beispiel #6
0
    def authenticate(self, remote_user, realm_subdomain=None):
        # type: (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 check_subdomain(realm_subdomain, user_profile.realm.subdomain):
            return None
        if not auth_enabled_helper([u"RemoteUser"], user_profile.realm):
            return None
        return user_profile
Beispiel #7
0
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, Text, 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)

        # 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)
        return response

    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()))
Beispiel #8
0
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)
Beispiel #9
0
    def authenticate(self, remote_user):
        if not remote_user:
            return

        email = remote_user_to_email(remote_user)

        try:
            user_profile = get_user_profile_by_email(email)
        except UserProfile.DoesNotExist:
            return None

        if user_profile.is_mirror_dummy:
            # mirror dummies can not login, but they can convert to real users
            return None

        return user_profile
Beispiel #10
0
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)
Beispiel #11
0
def login_or_register_remote_user(request, remote_username, user_profile, full_name='',
                                  invalid_subdomain=False):
    # type: (HttpRequest, text_type, UserProfile, text_type, Optional[bool]) -> HttpResponse
    if invalid_subdomain:
        # Show login page with an error message
        return redirect_to_subdomain_login_url()

    elif user_profile is None or user_profile.is_mirror_dummy:
        # Since execution has reached here, the client specified a remote user
        # but no associated user account exists. Send them over to the
        # PreregistrationUser flow.
        return maybe_send_to_registration(request, remote_user_to_email(remote_username), full_name)
    else:
        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()))
Beispiel #12
0
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)
Beispiel #13
0
    def authenticate(self, remote_user):
        if not remote_user:
            return None

        email = remote_user_to_email(remote_user)
        return common_get_active_user_by_email(email)
Beispiel #14
0
def login_or_register_remote_user(request: HttpRequest, remote_username: 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)
Beispiel #15
0
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)
Beispiel #16
0
    def authenticate(self, remote_user):
        if not remote_user:
            return None

        email = remote_user_to_email(remote_user)
        return common_get_active_user_by_email(email)
Beispiel #17
0
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)
Beispiel #18
0
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, Text, 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)

        # 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)
        return response

    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()))