Example #1
0
    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 "
                         u"deactivated. Please contact %s to reactivate "
                         u"it.") % (FromAddress.SUPPORT,)
            raise ValidationError(mark_safe(error_msg))

        if not check_subdomain(get_subdomain(self.request), user_profile.realm.subdomain):
            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
Example #2
0
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:
        user_profile = authenticate(username=username,
                                    password=password,
                                    realm_subdomain=get_subdomain(request),
                                    return_data=return_data)
    if return_data.get("inactive_user") == True:
        return json_error(_("Your account has been disabled."),
                          data={"reason": "user disable"}, status=403)
    if return_data.get("inactive_realm") == True:
        return json_error(_("Your realm has been deactivated."),
                          data={"reason": "realm deactivated"}, status=403)
    if return_data.get("password_auth_disabled") == True:
        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") == True:
            # 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})
Example #3
0
def create_homepage_form(request, user_info=None):
    # type: (HttpRequest, Optional[Dict[str, Any]]) -> HomepageForm
    if user_info:
        return HomepageForm(user_info, domain=request.session.get("domain"),
                            subdomain=get_subdomain(request))
    # An empty fields dict is not treated the same way as not
    # providing it.
    return HomepageForm(domain=request.session.get("domain"), subdomain=get_subdomain(request))
Example #4
0
        def _wrapped_func_arguments(request, api_key=REQ(),
                                    *args, **kwargs):
            # type: (HttpRequest, Text, *Any, **Any) -> HttpResponse
            try:
                user_profile = UserProfile.objects.get(api_key=api_key)
            except UserProfile.DoesNotExist:
                raise JsonableError(_("Invalid API key"))
            if not user_profile.is_active:
                raise JsonableError(_("Account not active"))
            if user_profile.realm.deactivated:
                raise JsonableError(_("Realm for account has been deactivated"))
            if not check_subdomain(get_subdomain(request), user_profile.realm.subdomain):
                logging.warning("User %s attempted to access webhook API on wrong subdomain %s" % (
                    user_profile.email, get_subdomain(request)))
                raise JsonableError(_("Account is not associated with this subdomain"))

            request.user = user_profile
            request._email = user_profile.email
            webhook_client_name = "Zulip{}Webhook".format(client_name)
            process_client(request, user_profile, client_name=webhook_client_name)
            if settings.RATE_LIMITING:
                rate_limit_user(request, user_profile, domain='all')
            try:
                return view_func(request, user_profile, *args, **kwargs)
            except Exception as err:
                if request.content_type == 'application/json':
                    try:
                        request_body = ujson.dumps(ujson.loads(request.body), indent=4)
                    except ValueError:
                        request_body = str(request.body)
                else:
                    request_body = str(request.body)
                message = """
user: {email} ({realm})
client: {client_name}
URL: {path_info}
content_type: {content_type}
body:

{body}
                """.format(
                    email=user_profile.email,
                    realm=user_profile.realm.string_id,
                    client_name=webhook_client_name,
                    body=request_body,
                    path_info=request.META.get('PATH_INFO', None),
                    content_type=request.content_type,
                )
                webhook_logger.exception(message)
                raise err
Example #5
0
def validate_api_key(request, role, api_key, is_webhook=False):
    # type: (HttpRequest, Text, Text, bool) -> Union[UserProfile, RemoteZulipServer]
    # Remove whitespace to protect users from trivial errors.
    role, api_key = role.strip(), api_key.strip()

    if not is_remote_server(role):
        try:
            profile = get_user_profile_by_email(role)  # type: Union[UserProfile, RemoteZulipServer]
        except UserProfile.DoesNotExist:
            raise JsonableError(_("Invalid user: %s") % (role,))
    else:
        try:
            profile = get_remote_server_by_uuid(role)
        except RemoteZulipServer.DoesNotExist:
            raise JsonableError(_("Invalid Zulip server: %s") % (role,))

    if api_key != profile.api_key:
        if len(api_key) != 32:
            reason = _("Incorrect API key length (keys should be 32 "
                       "characters long) for role '%s'")
        else:
            reason = _("Invalid API key for role '%s'")
        raise JsonableError(reason % (role,))

    # early exit for RemoteZulipServer instances
    if settings.ZILENCER_ENABLED and isinstance(profile, RemoteZulipServer):
        if not check_subdomain(get_subdomain(request), ""):
            raise JsonableError(_("This API key only works on the root subdomain"))
        return profile

    profile = cast(UserProfile, profile)  # is UserProfile
    if not profile.is_active:
        raise JsonableError(_("Account not active"))
    if profile.is_incoming_webhook and not is_webhook:
        raise JsonableError(_("Account is not valid to post webhook messages"))

    if profile.realm.deactivated:
        raise JsonableError(_("Realm for account has been deactivated"))

    if (not check_subdomain(get_subdomain(request), profile.realm.subdomain) and
        # Allow access to localhost for Tornado
        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 attempted to access API on wrong subdomain %s" % (
            profile.email, get_subdomain(request)))
        raise JsonableError(_("Account is not associated with this subdomain"))

    return profile
Example #6
0
    def do_auth(self, *args, **kwargs):
        # type: (*Any, **Any) -> Optional[HttpResponse]
        kwargs["return_data"] = {}

        request = self.strategy.request
        kwargs["realm_subdomain"] = get_subdomain(request)

        user_profile = None

        team_id = settings.SOCIAL_AUTH_GITHUB_TEAM_ID
        org_name = settings.SOCIAL_AUTH_GITHUB_ORG_NAME

        if team_id is None and org_name is None:
            user_profile = GithubOAuth2.do_auth(self, *args, **kwargs)

        elif team_id:
            backend = GithubTeamOAuth2(self.strategy, self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User profile not member of team.")
                user_profile = None

        elif org_name:
            backend = GithubOrganizationOAuth2(self.strategy, self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User profile not member of organisation.")
                user_profile = None

        return self.process_do_auth(user_profile, *args, **kwargs)
Example #7
0
def json_fetch_api_key(request, user_profile, password=REQ(default='')):
    # type: (HttpRequest, UserProfile, str) -> HttpResponse
    if password_auth_enabled(user_profile.realm):
        if not authenticate(username=user_profile.email, password=password,
                            realm_subdomain=get_subdomain(request)):
            return json_error(_("Your username or password is incorrect."))
    return json_success({"api_key": user_profile.api_key})
Example #8
0
    def send_mail(self, subject_template_name, email_template_name,
                  context, from_email, to_email, html_email_template_name=None):
        # type: (str, str, Dict[str, Any], str, str, str) -> None
        """
        Currently we don't support accounts in multiple subdomains using
        a single email addresss. 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 delete or refactor this
        function.
        """
        user_realm = get_user_profile_by_email(to_email).realm
        attempted_subdomain = get_subdomain(getattr(self, 'request'))
        context['attempted_realm'] = False
        if not check_subdomain(user_realm.subdomain, attempted_subdomain):
            context['attempted_realm'] = get_realm(attempted_subdomain)

        super(ZulipPasswordResetForm, self).send_mail(
            subject_template_name,
            email_template_name,
            context,
            from_email,
            to_email,
            html_email_template_name=html_email_template_name
        )
Example #9
0
def authenticate_log_and_execute_json(request, view_func, *args, **kwargs):
    # type: (HttpRequest, Callable[..., HttpResponse], *Any, **Any) -> HttpResponse
    if not request.user.is_authenticated():
        return json_error(_("Not logged in"), status=401)
    user_profile = request.user
    if not user_profile.is_active:
        raise JsonableError(_("Account not active"))
    if user_profile.realm.deactivated:
        raise JsonableError(_("Realm for account has been deactivated"))
    if user_profile.is_incoming_webhook:
        raise JsonableError(_("Webhook bots can only access webhooks"))
    if (
        not check_subdomain(get_subdomain(request), user_profile.realm.subdomain)
        and
        # Exclude the SOCKET requests from this filter; they were
        # checked when the original websocket request reached Tornado
        not (request.method == "SOCKET" and request.META["SERVER_NAME"] == "127.0.0.1")
    ):
        logging.warning(
            "User %s attempted to access JSON API on wrong subdomain %s" % (user_profile.email, get_subdomain(request))
        )
        raise JsonableError(_("Account is not associated with this subdomain"))

    process_client(request, user_profile, True)
    request._email = user_profile.email
    return view_func(request, user_profile, *args, **kwargs)
Example #10
0
def get_auth_backends_data(request):
    # type: (HttpRequest) -> Dict[str, Any]
    """Returns which authentication methods are enabled on the server"""
    if settings.REALMS_HAVE_SUBDOMAINS:
        subdomain = get_subdomain(request)
        try:
            realm = Realm.objects.get(string_id=subdomain)
        except Realm.DoesNotExist:
            # If not the root subdomain, this is an error
            if subdomain != "":
                raise JsonableError(_("Invalid subdomain"))
            # With the root subdomain, it's an error or not depending
            # whether SUBDOMAINS_HOMEPAGE (which indicates whether
            # there are some realms without subdomains on this server)
            # is set.
            if settings.SUBDOMAINS_HOMEPAGE:
                raise JsonableError(_("Subdomain required"))
            else:
                realm = None
    else:
        # Without subdomains, we just have to report what the server
        # supports, since we don't know the realm.
        realm = None
    return {"password": password_auth_enabled(realm),
            "dev": dev_auth_enabled(realm),
            "github": github_auth_enabled(realm),
            "google": google_auth_enabled(realm)}
Example #11
0
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)
    login(request, user_profile)
    return json_success({"api_key": user_profile.api_key, "email": user_profile.email})
Example #12
0
def log_into_subdomain(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        # Discard state if older than 15 seconds
        state = request.get_signed_cookie('subdomain.signature',
                                          salt='zerver.views.auth',
                                          max_age=15)
    except KeyError:
        logging.warning('Missing subdomain signature cookie.')
        return HttpResponse(status=400)
    except signing.BadSignature:
        logging.warning('Subdomain cookie has bad signature.')
        return HttpResponse(status=400)

    data = ujson.loads(state)
    if data['subdomain'] != get_subdomain(request):
        logging.warning('Login attemp on invalid subdomain')
        return HttpResponse(status=400)

    email_address = data['email']
    full_name = data['name']
    is_signup = data['is_signup']
    if is_signup:
        # If we are signing up, user_profile should be None. In case
        # email_address already exists, user will get an error message.
        user_profile = None
        return_data = {}  # type: Dict[str, Any]
    else:
        user_profile, return_data = authenticate_remote_user(request, email_address)
    invalid_subdomain = bool(return_data.get('invalid_subdomain'))
    return login_or_register_remote_user(request, email_address, user_profile,
                                         full_name, invalid_subdomain=invalid_subdomain,
                                         is_signup=is_signup)
Example #13
0
    def do_auth(self, *args, **kwargs):
        # type: (*Any, **Any) -> Optional[UserProfile]
        kwargs['return_data'] = {}

        request = self.strategy.request  # type: ignore # This comes from Python Social Auth.
        kwargs['realm_subdomain'] = get_subdomain(request)

        user_profile = None

        team_id = settings.SOCIAL_AUTH_GITHUB_TEAM_ID
        org_name = settings.SOCIAL_AUTH_GITHUB_ORG_NAME

        if (team_id is None and org_name is None):
            user_profile = GithubOAuth2.do_auth(self, *args, **kwargs)

        elif (team_id):
            backend = GithubTeamOAuth2(self.strategy, self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User profile not member of team.")
                user_profile = None

        elif (org_name):
            backend = GithubOrganizationOAuth2(self.strategy, self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User profile not member of organisation.")
                user_profile = None

        return self.process_do_auth(user_profile, *args, **kwargs)
Example #14
0
    def send_mail(self, subject_template_name, email_template_name,
                  context, from_email, to_email, html_email_template_name=None):
        # type: (str, str, Dict[str, Any], str, 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(getattr(self, 'request'))
        context['attempted_realm'] = False
        if not check_subdomain(user.realm.subdomain, attempted_subdomain):
            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)
Example #15
0
def log_into_subdomain(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        # Discard state if older than 15 seconds
        state = request.get_signed_cookie('subdomain.signature',
                                          salt='zerver.views.auth',
                                          max_age=15)
    except KeyError:
        logging.warning('Missing subdomain signature cookie.')
        return HttpResponse(status=400)
    except signing.BadSignature:
        logging.warning('Subdomain cookie has bad signature.')
        return HttpResponse(status=400)

    data = ujson.loads(state)
    if data['subdomain'] != get_subdomain(request):
        logging.warning('Login attemp on invalid subdomain')
        return HttpResponse(status=400)

    email_address = data['email']
    full_name = data['name']
    user_profile, return_data = authenticate_remote_user(request, email_address)
    invalid_subdomain = bool(return_data.get('invalid_subdomain'))
    return login_or_register_remote_user(request, email_address, user_profile,
                                         full_name, invalid_subdomain)
Example #16
0
def get_realm_from_request(request):
    # type: (HttpRequest) -> Realm
    if settings.REALMS_HAVE_SUBDOMAINS:
        realm_str = get_subdomain(request)
    else:
        realm_str = request.session.get("realm_str")
    return get_realm(realm_str)
Example #17
0
def authenticate_remote_user(request, email_address):
    # type: (HttpRequest, str) -> Tuple[UserProfile, Dict[str, Any]]
    return_data = {} # type: Dict[str, bool]
    user_profile = authenticate(username=email_address,
                                realm_subdomain=get_subdomain(request),
                                use_dummy_backend=True,
                                return_data=return_data)
    return user_profile, return_data
Example #18
0
def remote_user_sso(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        remote_user = request.META["REMOTE_USER"]
    except KeyError:
        raise JsonableError(_("No REMOTE_USER set."))

    user_profile = authenticate(remote_user=remote_user, realm_subdomain=get_subdomain(request))
    return login_or_register_remote_user(request, remote_user, user_profile)
Example #19
0
def get_realm_from_request(request):
    # type: (HttpRequest) -> Optional[Realm]
    if hasattr(request, "user") and hasattr(request.user, "realm"):
        return request.user.realm
    elif settings.REALMS_HAVE_SUBDOMAINS:
        subdomain = get_subdomain(request)
        return get_realm(subdomain)
    # This will return None if there is no unique, open realm.
    return get_unique_non_system_realm()
Example #20
0
def redirect_to_main_site(request, url):
    # type: (HttpRequest, Text) -> HttpResponse
    main_site_uri = ''.join((
        settings.EXTERNAL_URI_SCHEME,
        settings.EXTERNAL_HOST,
        url,
    ))
    params = {'subdomain': get_subdomain(request)}
    return redirect(main_site_uri + '?' + urllib.parse.urlencode(params))
Example #21
0
def start_google_oauth2(request):
    # type: (HttpRequest) -> HttpResponse
    main_site_uri = ''.join((
        settings.EXTERNAL_URI_SCHEME,
        settings.EXTERNAL_HOST,
        reverse('zerver.views.auth.send_oauth_request_to_google'),
    ))
    params = {'subdomain': get_subdomain(request)}
    return redirect(main_site_uri + '?' + urllib.parse.urlencode(params))
Example #22
0
def logged_in_and_active(request):
    # type: (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 check_subdomain(get_subdomain(request), request.user.realm.subdomain)
Example #23
0
    def process_response(self, request, response):
        # type: (HttpRequest, HttpResponse) -> HttpResponse
        if settings.REALMS_HAVE_SUBDOMAINS:
            if (not request.path.startswith("/static/") and not request.path.startswith("/api/")
                and not request.path.startswith("/json/")):
                subdomain = get_subdomain(request)
                if (request.get_host() == "127.0.0.1:9991" or request.get_host() == "localhost:9991"):
                    return redirect("%s%s" % (settings.EXTERNAL_URI_SCHEME,
                                              settings.EXTERNAL_HOST))
                if subdomain != "":
                    realm = resolve_subdomain_to_realm(subdomain)
                    if (realm is None):
                        return render_to_response("zerver/invalid_realm.html")
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
        except AttributeError:
            pass
        else:
            if accessed:
                patch_vary_headers(response, ('Cookie',))
            if modified or settings.SESSION_SAVE_EVERY_REQUEST:
                if request.session.get_expire_at_browser_close():
                    max_age = None
                    expires = None
                else:
                    max_age = request.session.get_expiry_age()
                    expires_time = time.time() + max_age
                    expires = cookie_date(expires_time)
                # Save the session data and refresh the client cookie.
                # Skip session save for 500 responses, refs #3881.
                if response.status_code != 500:
                    request.session.save()
                    host = request.get_host().split(':')[0]

                    session_cookie_domain = settings.SESSION_COOKIE_DOMAIN
                    # The subdomains feature overrides the
                    # SESSION_COOKIE_DOMAIN setting, since the setting
                    # is a fixed value and with subdomains enabled,
                    # the session cookie domain has to vary with the
                    # subdomain.
                    if settings.REALMS_HAVE_SUBDOMAINS:
                        session_cookie_domain = host
                    response.set_cookie(settings.SESSION_COOKIE_NAME,
                            request.session.session_key, max_age=max_age,
                            expires=expires, domain=session_cookie_domain,
                            path=settings.SESSION_COOKIE_PATH,
                            secure=settings.SESSION_COOKIE_SECURE or None,
                            httponly=settings.SESSION_COOKIE_HTTPONLY or None)
        return response
Example #24
0
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)

    # 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)
    return json_success({"api_key": user_profile.api_key, "email": user_profile.email})
Example #25
0
    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,
                settings.ZULIP_ADMINISTRATOR)
            raise ValidationError(mark_safe(error_msg))

        if not check_subdomain(get_subdomain(self.request), user_profile.realm.subdomain):
            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
Example #26
0
def validate_api_key(request, role, api_key, is_webhook=False):
    # type: (HttpRequest, text_type, text_type, bool) -> Union[UserProfile, Deployment]
    # Remove whitespace to protect users from trivial errors.
    role, api_key = role.strip(), api_key.strip()

    try:
        profile = get_deployment_or_userprofile(role)
    except UserProfile.DoesNotExist:
        raise JsonableError(_("Invalid user: %s") % (role,))
    except Deployment.DoesNotExist:
        raise JsonableError(_("Invalid deployment: %s") % (role,))

    if api_key != profile.api_key:
        if len(api_key) != 32:
            reason = _("Incorrect API key length (keys should be 32 "
                       "characters long) for role '%s'")
        else:
            reason = _("Invalid API key for role '%s'")
        raise JsonableError(reason % (role,))
    if not profile.is_active:
        raise JsonableError(_("Account not active"))
    if profile.is_incoming_webhook and not is_webhook:
        raise JsonableError(_("Account is not valid to post webhook messages"))
    try:
        if profile.realm.deactivated:
            raise JsonableError(_("Realm for account has been deactivated"))
    except AttributeError:
        # Deployment objects don't have realms
        pass
    if (not check_subdomain(get_subdomain(request), profile.realm.subdomain)
        # Allow access to localhost for Tornado
        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 attempted to access API on wrong subdomain %s" % (
            profile.email, get_subdomain(request)))
        raise JsonableError(_("Account is not associated with this subdomain"))

    return profile
Example #27
0
def home(request):
    # type: (HttpRequest) -> HttpResponse
    if not settings.SUBDOMAINS_HOMEPAGE:
        return home_real(request)

    # If settings.SUBDOMAINS_HOMEPAGE, sends the user the landing
    # page, not the login form, on the root domain

    subdomain = get_subdomain(request)
    if subdomain != "":
        return home_real(request)

    return render_to_response('zerver/hello.html',
                              request=request)
Example #28
0
        def _wrapped_func_arguments(request, api_key=REQ(),
                                    *args, **kwargs):
            # type: (HttpRequest, text_type, *Any, **Any) -> HttpResponse
            try:
                user_profile = UserProfile.objects.get(api_key=api_key)
            except UserProfile.DoesNotExist:
                raise JsonableError(_("Invalid API key"))
            if not user_profile.is_active:
                raise JsonableError(_("Account not active"))
            if user_profile.realm.deactivated:
                raise JsonableError(_("Realm for account has been deactivated"))
            if not check_subdomain(get_subdomain(request), user_profile.realm.subdomain):
                logging.warning("User %s attempted to access webhook API on wrong subdomain %s" % (
                    user_profile.email, get_subdomain(request)))
                raise JsonableError(_("Account is not associated with this subdomain"))

            request.user = user_profile
            request._email = user_profile.email
            webhook_client_name = "Zulip{}Webhook".format(client_name)
            process_client(request, user_profile, client_name=webhook_client_name)
            if settings.RATE_LIMITING:
                rate_limit_user(request, user_profile, domain='all')
            return view_func(request, user_profile, request.client, *args, **kwargs)
Example #29
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)
Example #30
0
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")
    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()))
Example #31
0
    def do_auth(self, *args, **kwargs):
        # type: (*Any, **Any) -> Optional[HttpResponse]
        kwargs['return_data'] = {}

        request = self.strategy.request
        kwargs['realm_subdomain'] = get_subdomain(request)

        user_profile = None

        team_id = settings.SOCIAL_AUTH_GITHUB_TEAM_ID
        org_name = settings.SOCIAL_AUTH_GITHUB_ORG_NAME

        if (team_id is None and org_name is None):
            try:
                user_profile = GithubOAuth2.do_auth(self, *args, **kwargs)
            except AuthFailed:
                logging.info("User authentication failed.")
                user_profile = None

        elif (team_id):
            backend = GithubTeamOAuth2(self.strategy, self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User is not member of GitHub team.")
                user_profile = None

        elif (org_name):
            backend = GithubOrganizationOAuth2(self.strategy,
                                               self.redirect_uri)
            try:
                user_profile = backend.do_auth(*args, **kwargs)
            except AuthFailed:
                logging.info("User is not member of GitHub organization.")
                user_profile = None

        return self.process_do_auth(user_profile, *args, **kwargs)
Example #32
0
def log_into_subdomain(request):
    # type: (HttpRequest) -> HttpResponse
    try:
        # Discard state if older than 15 seconds
        state = request.get_signed_cookie('subdomain.signature',
                                          salt='zerver.views.auth',
                                          max_age=15)
    except KeyError:
        logging.warning('Missing subdomain signature cookie.')
        return HttpResponse(status=400)
    except signing.BadSignature:
        logging.warning('Subdomain cookie has bad signature.')
        return HttpResponse(status=400)

    data = ujson.loads(state)
    if data['subdomain'] != get_subdomain(request):
        logging.warning('Login attemp on invalid subdomain')
        return HttpResponse(status=400)

    email_address = data['email']
    full_name = data['name']
    is_signup = data['is_signup']
    if is_signup:
        # If we are signing up, user_profile should be None. In case
        # email_address already exists, user will get an error message.
        user_profile = None
        return_data = {}  # type: Dict[str, Any]
    else:
        user_profile, return_data = authenticate_remote_user(
            request, email_address)
    invalid_subdomain = bool(return_data.get('invalid_subdomain'))
    return login_or_register_remote_user(request,
                                         email_address,
                                         user_profile,
                                         full_name,
                                         invalid_subdomain=invalid_subdomain,
                                         is_signup=is_signup)
Example #33
0
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
    })
Example #34
0
def get_auth_backends_data(request):
    # type: (HttpRequest) -> Dict[str, Any]
    """Returns which authentication methods are enabled on the server"""
    subdomain = get_subdomain(request)
    try:
        realm = Realm.objects.get(string_id=subdomain)
    except Realm.DoesNotExist:
        # If not the root subdomain, this is an error
        if subdomain != "":
            raise JsonableError(_("Invalid subdomain"))
        # With the root subdomain, it's an error or not depending
        # whether ROOT_DOMAIN_LANDING_PAGE (which indicates whether
        # there are some realms without subdomains on this server)
        # is set.
        if settings.ROOT_DOMAIN_LANDING_PAGE:
            raise JsonableError(_("Subdomain required"))
        else:
            realm = None
    return {
        "password": password_auth_enabled(realm),
        "dev": dev_auth_enabled(realm),
        "github": github_auth_enabled(realm),
        "google": google_auth_enabled(realm)
    }
Example #35
0
    def process_response(self, request, response):
        # type: (HttpRequest, HttpResponse) -> HttpResponse
        try:
            request.get_host()
        except DisallowedHost:
            # If we get a DisallowedHost exception trying to access
            # the host, (1) the request is failed anyway and so the
            # below code will do nothing, and (2) the below will
            # trigger a recursive exception, breaking things, so we
            # just return here.
            return response

        if settings.REALMS_HAVE_SUBDOMAINS:
            if (not request.path.startswith("/static/")
                    and not request.path.startswith("/api/")
                    and not request.path.startswith("/json/")):
                subdomain = get_subdomain(request)
                if (request.get_host() == "127.0.0.1:9991"
                        or request.get_host() == "localhost:9991"):
                    return redirect(
                        "%s%s" %
                        (settings.EXTERNAL_URI_SCHEME, settings.EXTERNAL_HOST))
                if subdomain != "":
                    realm = get_realm(subdomain)
                    if (realm is None):
                        return render(request, "zerver/invalid_realm.html")
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
        except AttributeError:
            pass
        else:
            if accessed:
                patch_vary_headers(response, ('Cookie', ))
            if modified or settings.SESSION_SAVE_EVERY_REQUEST:
                if request.session.get_expire_at_browser_close():
                    max_age = None
                    expires = None
                else:
                    max_age = request.session.get_expiry_age()
                    expires_time = time.time() + max_age
                    expires = cookie_date(expires_time)
                # Save the session data and refresh the client cookie.
                # Skip session save for 500 responses, refs #3881.
                if response.status_code != 500:
                    request.session.save()
                    host = request.get_host().split(':')[0]

                    session_cookie_domain = settings.SESSION_COOKIE_DOMAIN
                    # The subdomains feature overrides the
                    # SESSION_COOKIE_DOMAIN setting, since the setting
                    # is a fixed value and with subdomains enabled,
                    # the session cookie domain has to vary with the
                    # subdomain.
                    if settings.REALMS_HAVE_SUBDOMAINS:
                        session_cookie_domain = host
                    response.set_cookie(
                        settings.SESSION_COOKIE_NAME,
                        request.session.session_key,
                        max_age=max_age,
                        expires=expires,
                        domain=session_cookie_domain,
                        path=settings.SESSION_COOKIE_PATH,
                        secure=settings.SESSION_COOKIE_SECURE or None,
                        httponly=settings.SESSION_COOKIE_HTTPONLY or None)
        return response
Example #36
0
def zulip_default_context(request):
    # type: (HttpRequest) -> Dict[str, Any]
    """Context available to all Zulip Jinja2 templates that have a request
    passed in.  Designed to provide the long list of variables at the
    bottom of this function in a wide range of situations: logged-in
    or logged-out, subdomains or not, etc.

    The main variable in the below is whether we know what realm the
    user is trying to interact with.
    """
    realm = get_realm_from_request(request)

    if realm is None:
        realm_uri = settings.ROOT_DOMAIN_URI
        realm_name = None
        realm_icon = None
        realm_description = None
        realm_invite_required = False
    else:
        realm_uri = realm.uri
        realm_name = realm.name
        realm_icon = get_realm_icon_url(realm)
        realm_description_raw = realm.description or "The coolest place in the universe."
        realm_description = convert(realm_description_raw, message_realm=realm)
        realm_invite_required = realm.invite_required

    register_link_disabled = settings.REGISTER_LINK_DISABLED
    login_link_disabled = settings.LOGIN_LINK_DISABLED
    about_link_disabled = settings.ABOUT_LINK_DISABLED
    find_team_link_disabled = settings.FIND_TEAM_LINK_DISABLED

    if settings.ROOT_DOMAIN_LANDING_PAGE and get_subdomain(request) == "":
        register_link_disabled = True
        login_link_disabled = True
        about_link_disabled = True
        find_team_link_disabled = False

    apps_page_url = 'https://zulipchat.com/apps/'
    if settings.ZILENCER_ENABLED:
        apps_page_url = '/apps/'

    user_is_authenticated = False
    if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated'):
        user_is_authenticated = request.user.is_authenticated.value

    if settings.DEVELOPMENT:
        secrets_path = "zproject/dev-secrets.conf"
        settings_path = "zproject/dev_settings.py"
        settings_comments_path = "zproject/prod_settings_template.py"
    else:
        secrets_path = "/etc/zulip/zulip-secrets.conf"
        settings_path = "/etc/zulip/settings.py"
        settings_comments_path = "/etc/zulip/settings.py"

    if hasattr(request, "client") and request.client.name == "ZulipElectron":
        platform = "ZulipElectron"  # nocoverage
    else:
        platform = "ZulipWeb"

    return {
        'root_domain_landing_page':
        settings.ROOT_DOMAIN_LANDING_PAGE,
        'custom_logo_url':
        settings.CUSTOM_LOGO_URL,
        'register_link_disabled':
        register_link_disabled,
        'login_link_disabled':
        login_link_disabled,
        'about_link_disabled':
        about_link_disabled,
        'terms_of_service':
        settings.TERMS_OF_SERVICE,
        'privacy_policy':
        settings.PRIVACY_POLICY,
        'login_url':
        settings.HOME_NOT_LOGGED_IN,
        'only_sso':
        settings.ONLY_SSO,
        'external_api_path':
        settings.EXTERNAL_API_PATH,
        'external_api_uri':
        settings.EXTERNAL_API_URI,
        'external_host':
        settings.EXTERNAL_HOST,
        'external_uri_scheme':
        settings.EXTERNAL_URI_SCHEME,
        'realm_invite_required':
        realm_invite_required,
        'realm_uri':
        realm_uri,
        'realm_name':
        realm_name,
        'realm_icon':
        realm_icon,
        'realm_description':
        realm_description,
        'root_domain_uri':
        settings.ROOT_DOMAIN_URI,
        'api_site_required':
        settings.EXTERNAL_API_PATH != "api.zulip.com",
        'email_gateway_example':
        settings.EMAIL_GATEWAY_EXAMPLE,
        'apps_page_url':
        apps_page_url,
        'open_realm_creation':
        settings.OPEN_REALM_CREATION,
        'password_auth_enabled':
        password_auth_enabled(realm),
        'dev_auth_enabled':
        dev_auth_enabled(realm),
        'google_auth_enabled':
        google_auth_enabled(realm),
        'github_auth_enabled':
        github_auth_enabled(realm),
        'email_auth_enabled':
        email_auth_enabled(realm),
        'require_email_format_usernames':
        require_email_format_usernames(realm),
        'any_oauth_backend_enabled':
        any_oauth_backend_enabled(realm),
        'no_auth_enabled':
        not auth_enabled_helper(list(AUTH_BACKEND_NAME_MAP.keys()), realm),
        'development_environment':
        settings.DEVELOPMENT,
        'support_email':
        FromAddress.SUPPORT,
        'find_team_link_disabled':
        find_team_link_disabled,
        'password_min_length':
        settings.PASSWORD_MIN_LENGTH,
        'password_min_guesses':
        settings.PASSWORD_MIN_GUESSES,
        'zulip_version':
        ZULIP_VERSION,
        'user_is_authenticated':
        user_is_authenticated,
        'settings_path':
        settings_path,
        'secrets_path':
        secrets_path,
        'settings_comments_path':
        settings_comments_path,
        'platform':
        platform,
    }
Example #37
0
def get_realm_from_request(request):
    # type: (HttpRequest) -> Optional[Realm]
    if hasattr(request, "user") and hasattr(request.user, "realm"):
        return request.user.realm
    subdomain = get_subdomain(request)
    return get_realm(subdomain)
Example #38
0
def zulip_default_context(request):
    # type: (HttpRequest) -> Dict[str, Any]
    """Context available to all Zulip Jinja2 templates that have a request
    passed in.  Designed to provide the long list of variables at the
    bottom of this function in a wide range of situations: logged-in
    or logged-out, subdomains or not, etc.

    The main variable in the below is whether we know the realm, which
    is the case if there is only one realm, or we're on a
    REALMS_HAVE_SUBDOMAINS subdomain, or the user is logged in.
    """
    realm = get_realm_from_request(request)

    if realm is not None:
        realm_uri = realm.uri
        realm_name = realm.name
        realm_icon = get_realm_icon_url(realm)
        realm_description_raw = realm.description or "The coolest place in the universe."
        realm_description = convert(realm_description_raw, message_realm=realm)
    else:
        realm_uri = settings.SERVER_URI
        realm_name = None
        realm_icon = None
        realm_description = None

    register_link_disabled = settings.REGISTER_LINK_DISABLED
    login_link_disabled = settings.LOGIN_LINK_DISABLED
    about_link_disabled = settings.ABOUT_LINK_DISABLED
    find_team_link_disabled = settings.FIND_TEAM_LINK_DISABLED
    if settings.SUBDOMAINS_HOMEPAGE and get_subdomain(request) == "":
        register_link_disabled = True
        login_link_disabled = True
        about_link_disabled = True
        find_team_link_disabled = False

    return {
        'realms_have_subdomains':
        settings.REALMS_HAVE_SUBDOMAINS,
        'custom_logo_url':
        settings.CUSTOM_LOGO_URL,
        'register_link_disabled':
        register_link_disabled,
        'login_link_disabled':
        login_link_disabled,
        'about_link_disabled':
        about_link_disabled,
        'show_oss_announcement':
        settings.SHOW_OSS_ANNOUNCEMENT,
        'zulip_admin':
        settings.ZULIP_ADMINISTRATOR,
        'terms_of_service':
        settings.TERMS_OF_SERVICE,
        'privacy_policy':
        settings.PRIVACY_POLICY,
        'login_url':
        settings.HOME_NOT_LOGGED_IN,
        'only_sso':
        settings.ONLY_SSO,
        'external_api_path':
        settings.EXTERNAL_API_PATH,
        'external_api_uri':
        settings.EXTERNAL_API_URI,
        'external_host':
        settings.EXTERNAL_HOST,
        'external_uri_scheme':
        settings.EXTERNAL_URI_SCHEME,
        'realm_uri':
        realm_uri,
        'realm_name':
        realm_name,
        'realm_icon':
        realm_icon,
        'realm_description':
        realm_description,
        'server_uri':
        settings.SERVER_URI,
        'api_site_required':
        settings.EXTERNAL_API_PATH != "api.zulip.com",
        'email_gateway_example':
        settings.EMAIL_GATEWAY_EXAMPLE,
        'open_realm_creation':
        settings.OPEN_REALM_CREATION,
        'password_auth_enabled':
        password_auth_enabled(realm),
        'dev_auth_enabled':
        dev_auth_enabled(realm),
        'google_auth_enabled':
        google_auth_enabled(realm),
        'github_auth_enabled':
        github_auth_enabled(realm),
        'any_oauth_backend_enabled':
        any_oauth_backend_enabled(realm),
        'no_auth_enabled':
        not auth_enabled_helper(list(AUTH_BACKEND_NAME_MAP.keys()), realm),
        'development_environment':
        settings.DEVELOPMENT,
        'support_email':
        settings.ZULIP_ADMINISTRATOR,
        'find_team_link_disabled':
        find_team_link_disabled,
        'password_min_length':
        settings.PASSWORD_MIN_LENGTH,
        'password_min_quality':
        settings.PASSWORD_MIN_ZXCVBN_QUALITY,
        'zulip_version':
        ZULIP_VERSION,
    }
Example #39
0
def finish_google_oauth2(request):
    # type: (HttpRequest) -> HttpResponse
    error = request.GET.get('error')
    if error == 'access_denied':
        return redirect('/')
    elif error is not None:
        logging.warning('Error from google oauth2 login: %s' %
                        (request.GET.get("error"), ))
        return HttpResponse(status=400)

    csrf_state = request.GET.get('state')
    if csrf_state is None or len(csrf_state.split(':')) != 2:
        logging.warning('Missing Google oauth2 CSRF state')
        return HttpResponse(status=400)

    value, hmac_value = csrf_state.split(':')
    if hmac_value != google_oauth2_csrf(request, value):
        logging.warning('Google oauth2 CSRF error')
        return HttpResponse(status=400)

    resp = requests.post(
        'https://www.googleapis.com/oauth2/v3/token',
        data={
            'code':
            request.GET.get('code'),
            'client_id':
            settings.GOOGLE_OAUTH2_CLIENT_ID,
            'client_secret':
            settings.GOOGLE_OAUTH2_CLIENT_SECRET,
            'redirect_uri':
            ''.join((
                settings.EXTERNAL_URI_SCHEME,
                request.get_host(),
                reverse('zerver.views.auth.finish_google_oauth2'),
            )),
            'grant_type':
            'authorization_code',
        },
    )
    if resp.status_code == 400:
        logging.warning(
            'User error converting Google oauth2 login to token: %s' %
            (resp.text, ))
        return HttpResponse(status=400)
    elif resp.status_code != 200:
        logging.error(
            'Could not convert google oauth2 code to access_token: %s' %
            (resp.text, ))
        return HttpResponse(status=400)
    access_token = resp.json()['access_token']

    resp = requests.get('https://www.googleapis.com/plus/v1/people/me',
                        params={'access_token': access_token})
    if resp.status_code == 400:
        logging.warning('Google login failed making info API call: %s' %
                        (resp.text, ))
        return HttpResponse(status=400)
    elif resp.status_code != 200:
        logging.error('Google login failed making API call: %s' %
                      (resp.text, ))
        return HttpResponse(status=400)
    body = resp.json()

    try:
        full_name = body['name']['formatted']
    except KeyError:
        # Only google+ users have a formated name. I am ignoring i18n here.
        full_name = u'{} {}'.format(body['name']['givenName'],
                                    body['name']['familyName'])
    for email in body['emails']:
        if email['type'] == 'account':
            break
    else:
        logging.error('Google oauth2 account email not found: %s' % (body, ))
        return HttpResponse(status=400)
    email_address = email['value']
    return_data = {}  # type: Dict[str, bool]
    user_profile = authenticate(username=email_address,
                                realm_subdomain=get_subdomain(request),
                                use_dummy_backend=True,
                                return_data=return_data)

    invalid_subdomain = bool(return_data.get('invalid_subdomain'))
    return login_or_register_remote_user(request, email_address, user_profile,
                                         full_name, invalid_subdomain)
Example #40
0
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
    try:
        existing_user_profile = get_user_profile_by_email(email)
    except UserProfile.DoesNotExist:
        existing_user_profile = None

    validators.validate_email(email)
    # If OPEN_REALM_CREATION is enabled all user sign ups should go through the
    # special URL with domain name so that REALM can be identified if multiple realms exist
    unique_open_realm = get_unique_open_realm()
    if unique_open_realm is not None:
        realm = unique_open_realm
    elif 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 prereg_user.realm:
        # You have a realm set, even though nobody referred you. This
        # happens if you sign up through a special URL for an open realm.
        realm = prereg_user.realm
    elif realm_creation:
        # For creating a new realm, there is no existing realm or domain
        realm = None
    elif settings.REALMS_HAVE_SUBDOMAINS:
        realm = get_realm(get_subdomain(request))
    else:
        realm = get_realm_by_email_domain(email)

    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 render(request,
                      "zerver/deactivated.html",
                      context={
                          "deactivated_domain_name": realm.name,
                          "zulip_administrator": settings.ZULIP_ADMINISTRATOR
                      })

    try:
        if existing_user_profile is not None and existing_user_profile.is_mirror_dummy:
            # Mirror dummy users to be activated must be inactive
            is_inactive(email)
        else:
            # Other users should not already exist at all.
            user_email_is_unique(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 ""
                })
            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})
                        # 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()
        elif 'full_name' in request.POST:
            form = RegistrationForm(
                initial={'full_name': request.POST.get('full_name')})
        else:
            form = RegistrationForm()
    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)
        if not password_auth_enabled(realm):
            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']
            org_type = int(form.cleaned_data['realm_org_type'])
            realm = do_create_realm(string_id, realm_name,
                                    org_type=org_type)[0]

            set_default_streams(realm, settings.DEFAULT_NEW_REALM_STREAMS)

        full_name = form.cleaned_data['full_name']
        short_name = email_to_username(email)
        first_in_realm = len(
            UserProfile.objects.filter(realm=realm, is_bot=False)) == 0

        # FIXME: sanitize email addresses and fullname
        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)
        else:
            user_profile = do_create_user(
                email,
                password,
                realm,
                full_name,
                short_name,
                prereg_user=prereg_user,
                tos_version=settings.TOS_VERSION,
                newsletter_data={"IP": request.META['REMOTE_ADDR']})

        if first_in_realm:
            do_change_is_admin(user_profile, True)

        if realm_creation and settings.REALMS_HAVE_SUBDOMAINS:
            # 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('/')
        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,
            'realms_have_subdomains': settings.REALMS_HAVE_SUBDOMAINS,
            '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)
        })