コード例 #1
0
ファイル: registration.py プロジェクト: yushao2/zulip
def redirect_to_email_login_url(email: str) -> HttpResponseRedirect:
    login_url = reverse("login")
    redirect_url = add_query_to_redirect_url(
        login_url, urlencode({
            "email": email,
            "already_registered": 1
        }))
    return HttpResponseRedirect(redirect_url)
コード例 #2
0
def find_account(request: HttpRequest) -> HttpResponse:
    from zerver.context_processors import common_context

    url = reverse("find_account")

    emails: List[str] = []
    if request.method == "POST":
        form = FindMyTeamForm(request.POST)
        if form.is_valid():
            emails = form.cleaned_data["emails"]

            # Django doesn't support __iexact__in lookup with EmailField, so we have
            # to use Qs to get around that without needing to do multiple queries.
            emails_q = Q()
            for email in emails:
                emails_q |= Q(delivery_email__iexact=email)

            for user in UserProfile.objects.filter(emails_q,
                                                   is_active=True,
                                                   is_bot=False,
                                                   realm__deactivated=False):
                context = common_context(user)
                context.update(email=user.delivery_email, )
                send_email(
                    "zerver/emails/find_team",
                    to_user_ids=[user.id],
                    context=context,
                    from_address=FromAddress.SUPPORT,
                )

            # Note: Show all the emails in the result otherwise this
            # feature can be used to ascertain which email addresses
            # are associated with Zulip.
            data = urllib.parse.urlencode({"emails": ",".join(emails)})
            return redirect(add_query_to_redirect_url(url, data))
    else:
        form = FindMyTeamForm()
        result = request.GET.get("emails")
        # The below validation is perhaps unnecessary, in that we
        # shouldn't get able to get here with an invalid email unless
        # the user hand-edits the URLs.
        if result:
            for email in result.split(","):
                try:
                    validators.validate_email(email)
                    emails.append(email)
                except ValidationError:
                    pass

    return render(
        request,
        "zerver/find_account.html",
        context={
            "form": form,
            "current_url": lambda: url,
            "emails": emails
        },
    )
コード例 #3
0
def start_remote_user_sso(request: HttpRequest) -> HttpResponse:
    """
    The purpose of this endpoint is to provide an initial step in the flow
    on which we can handle the special behavior for the desktop app.
    /accounts/login/sso may have Apache intercepting requests to it
    to do authentication, so we need this additional endpoint.
    """
    query = request.META["QUERY_STRING"]
    return redirect(add_query_to_redirect_url(reverse(remote_user_sso), query))
コード例 #4
0
ファイル: auth.py プロジェクト: wedataintelligence/zulip
def create_response_for_otp_flow(key: str, otp: str, user_profile: UserProfile,
                                 encrypted_key_field_name: str) -> HttpResponse:
    params = {
        encrypted_key_field_name: otp_encrypt_api_key(key, otp),
        'email': user_profile.delivery_email,
        'realm': user_profile.realm.uri,
    }
    # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs
    response = HttpResponse(status=302)
    response['Location'] = add_query_to_redirect_url('zulip://login', urllib.parse.urlencode(params))

    return response
コード例 #5
0
ファイル: video_calls.py プロジェクト: swapniltri/zulip
def get_bigbluebutton_url(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
    # https://docs.bigbluebutton.org/dev/api.html#create for reference on the API calls
    # https://docs.bigbluebutton.org/dev/api.html#usage for reference for checksum
    id = "zulip-" + str(random.randint(100000000000, 999999999999))
    password = b32encode(secrets.token_bytes(7))[:10].decode()
    checksum = hashlib.sha1(("create" + "meetingID=" + id + "&moderatorPW="
                             + password + "&attendeePW=" + password + "a" + settings.BIG_BLUE_BUTTON_SECRET).encode()).hexdigest()
    url = add_query_to_redirect_url("/calls/bigbluebutton/join", urlencode({
        "meeting_id": "\"" + id + "\"",
        "password": "******"" + password + "\"",
        "checksum": "\"" + checksum + "\""
    }))
    return json_success({"url": url})
コード例 #6
0
ファイル: registration.py プロジェクト: veekram/zulip
def find_account(request: HttpRequest) -> HttpResponse:
    from zerver.context_processors import common_context
    url = reverse('zerver.views.registration.find_account')

    emails: List[str] = []
    if request.method == 'POST':
        form = FindMyTeamForm(request.POST)
        if form.is_valid():
            emails = form.cleaned_data['emails']
            for user in UserProfile.objects.filter(delivery_email__in=emails,
                                                   is_active=True,
                                                   is_bot=False,
                                                   realm__deactivated=False):
                context = common_context(user)
                context.update({
                    'email': user.delivery_email,
                })
                send_email('zerver/emails/find_team',
                           to_user_ids=[user.id],
                           context=context,
                           from_address=FromAddress.SUPPORT)

            # Note: Show all the emails in the result otherwise this
            # feature can be used to ascertain which email addresses
            # are associated with Zulip.
            data = urllib.parse.urlencode({'emails': ','.join(emails)})
            return redirect(add_query_to_redirect_url(url, data))
    else:
        form = FindMyTeamForm()
        result = request.GET.get('emails')
        # The below validation is perhaps unnecessary, in that we
        # shouldn't get able to get here with an invalid email unless
        # the user hand-edits the URLs.
        if result:
            for email in result.split(','):
                try:
                    validators.validate_email(email)
                    emails.append(email)
                except ValidationError:
                    pass

    return render(
        request,
        'zerver/find_account.html',
        context={
            'form': form,
            'current_url': lambda: url,
            'emails': emails
        },
    )
コード例 #7
0
ファイル: auth.py プロジェクト: pastewka/zulip
def password_reset(request: HttpRequest) -> HttpResponse:
    if is_subdomain_root_or_alias(request) and settings.ROOT_DOMAIN_LANDING_PAGE:
        redirect_url = add_query_to_redirect_url(
            reverse("realm_redirect"), urlencode({"next": reverse("password_reset")})
        )
        return HttpResponseRedirect(redirect_url)

    response = DjangoPasswordResetView.as_view(
        template_name="zerver/reset.html",
        form_class=ZulipPasswordResetForm,
        success_url="/accounts/password/reset/done/",
    )(request)
    assert isinstance(response, HttpResponse)
    return response
コード例 #8
0
ファイル: video_calls.py プロジェクト: swapniltri/zulip
def join_bigbluebutton(request: HttpRequest, meeting_id: str = REQ(validator=check_string),
                       password: str = REQ(validator=check_string),
                       checksum: str = REQ(validator=check_string)) -> HttpResponse:
    if settings.BIG_BLUE_BUTTON_URL is None or settings.BIG_BLUE_BUTTON_SECRET is None:
        return json_error(_("Big Blue Button is not configured."))
    else:
        try:
            response = requests.get(
                add_query_to_redirect_url(settings.BIG_BLUE_BUTTON_URL + "api/create", urlencode({
                    "meetingID": meeting_id,
                    "moderatorPW": password,
                    "attendeePW": password + "a",
                    "checksum": checksum
                })))
            response.raise_for_status()
        except requests.RequestException:
            return json_error(_("Error connecting to the Big Blue Button server."))

        payload = ElementTree.fromstring(response.text)
        if payload.find("messageKey").text == "checksumError":
            return json_error(_("Error authenticating to the Big Blue Button server."))

        if payload.find("returncode").text != "SUCCESS":
            return json_error(_("Big Blue Button server returned an unexpected error."))

        join_params = urlencode(  # type: ignore[type-var] # https://github.com/python/typeshed/issues/4234
            {
                "meetingID": meeting_id,
                "password": password,
                "fullName": request.user.full_name,
            },
            quote_via=quote,
        )

        checksum = hashlib.sha1(("join" + join_params + settings.BIG_BLUE_BUTTON_SECRET).encode()).hexdigest()
        redirect_url_base = add_query_to_redirect_url(settings.BIG_BLUE_BUTTON_URL + "api/join", join_params)
        return redirect(add_query_arg_to_redirect_url(redirect_url_base, "checksum=" + checksum))
コード例 #9
0
def create_response_for_otp_flow(key: str, otp: str, user_profile: UserProfile,
                                 encrypted_key_field_name: str) -> HttpResponse:
    realm_uri = user_profile.realm.uri

    # Check if the mobile URI is overridden in settings, if so, replace it
    # This block should only apply to the mobile flow, so we if add others, this
    # needs to be conditional.
    if realm_uri in settings.REALM_MOBILE_REMAP_URIS:
        realm_uri = settings.REALM_MOBILE_REMAP_URIS[realm_uri]

    params = {
        encrypted_key_field_name: otp_encrypt_api_key(key, otp),
        'email': user_profile.delivery_email,
        'realm': realm_uri,
    }
    # We can't use HttpResponseRedirect, since it only allows HTTP(S) URLs
    response = HttpResponse(status=302)
    response['Location'] = add_query_to_redirect_url('zulip://login', urllib.parse.urlencode(params))

    return response
コード例 #10
0
ファイル: video_calls.py プロジェクト: winning1120xx/zulip
def get_bigbluebutton_url(request: HttpRequest,
                          user_profile: UserProfile) -> HttpResponse:
    # https://docs.bigbluebutton.org/dev/api.html#create for reference on the api calls
    # https://docs.bigbluebutton.org/dev/api.html#usage for reference for checksum
    id = "zulip-" + str(random.randint(100000000000, 999999999999))
    password = ''.join(random.SystemRandom().choice(string.ascii_uppercase +
                                                    string.digits)
                       for _ in range(10))
    checksum = hashlib.sha1(
        ("create" + "meetingID=" + id + "&moderatorPW=" + password +
         "&attendeePW=" + password + "a" +
         settings.BIG_BLUE_BUTTON_SECRET).encode()).hexdigest()
    url = add_query_to_redirect_url(
        "/calls/bigbluebutton/join",
        urlencode({
            "meeting_id": "\"" + id + "\"",
            "password": "******"" + password + "\"",
            "checksum": "\"" + checksum + "\""
        }))
    return json_success({"url": url})
コード例 #11
0
def oauth_redirect_to_root(
    request: HttpRequest,
    url: str,
    sso_type: str,
    is_signup: bool = False,
    extra_url_params: Dict[str, str] = {},
    next: Optional[str] = REQ(default=None),
) -> HttpResponse:
    main_site_uri = settings.ROOT_DOMAIN_URI + url
    if settings.SOCIAL_AUTH_SUBDOMAIN is not None and sso_type == "social":
        main_site_uri = (
            settings.EXTERNAL_URI_SCHEME
            + settings.SOCIAL_AUTH_SUBDOMAIN
            + "."
            + settings.EXTERNAL_HOST
        ) + url

    params = {
        "subdomain": get_subdomain(request),
        "is_signup": "1" if is_signup else "0",
    }

    params["multiuse_object_key"] = request.GET.get("multiuse_object_key", "")

    # mobile_flow_otp is a one-time pad provided by the app that we
    # can use to encrypt the API key when passing back to the app.
    mobile_flow_otp = request.GET.get("mobile_flow_otp")
    desktop_flow_otp = request.GET.get("desktop_flow_otp")

    validate_otp_params(mobile_flow_otp, desktop_flow_otp)
    if mobile_flow_otp is not None:
        params["mobile_flow_otp"] = mobile_flow_otp
    if desktop_flow_otp is not None:
        params["desktop_flow_otp"] = desktop_flow_otp

    if next:
        params["next"] = next

    params = {**params, **extra_url_params}

    return redirect(add_query_to_redirect_url(main_site_uri, urllib.parse.urlencode(params)))
コード例 #12
0
ファイル: auth.py プロジェクト: CohenCyril/zulip
def oauth_redirect_to_root(
        request: HttpRequest,
        url: str,
        sso_type: str,
        is_signup: bool = False,
        extra_url_params: Dict[str, str] = {}) -> HttpResponse:
    main_site_uri = settings.ROOT_DOMAIN_URI + url
    if settings.SOCIAL_AUTH_SUBDOMAIN is not None and sso_type == 'social':
        main_site_uri = (settings.EXTERNAL_URI_SCHEME +
                         settings.SOCIAL_AUTH_SUBDOMAIN + "." +
                         settings.EXTERNAL_HOST) + url

    params = {
        'subdomain': get_subdomain(request),
        'is_signup': '1' if is_signup else '0',
    }

    params['multiuse_object_key'] = request.GET.get('multiuse_object_key', '')

    # mobile_flow_otp is a one-time pad provided by the app that we
    # can use to encrypt the API key when passing back to the app.
    mobile_flow_otp = request.GET.get('mobile_flow_otp')
    desktop_flow_otp = request.GET.get('desktop_flow_otp')

    validate_otp_params(mobile_flow_otp, desktop_flow_otp)
    if mobile_flow_otp is not None:
        params['mobile_flow_otp'] = mobile_flow_otp
    if desktop_flow_otp is not None:
        params['desktop_flow_otp'] = desktop_flow_otp

    next = request.GET.get('next')
    if next:
        params['next'] = next

    params = {**params, **extra_url_params}

    return redirect(
        add_query_to_redirect_url(main_site_uri,
                                  urllib.parse.urlencode(params)))
コード例 #13
0
def login_page(
    request: HttpRequest,
    next: str = REQ(default="/"),
    **kwargs: Any,
) -> HttpResponse:
    # To support previewing the Zulip login pages, we have a special option
    # that disables the default behavior of redirecting logged-in users to the
    # logged-in app.
    is_preview = "preview" in request.GET
    if settings.TWO_FACTOR_AUTHENTICATION_ENABLED:
        if request.user and request.user.is_verified():
            return HttpResponseRedirect(request.user.realm.uri)
    elif request.user.is_authenticated and not is_preview:
        return HttpResponseRedirect(request.user.realm.uri)
    if is_subdomain_root_or_alias(request) and settings.ROOT_DOMAIN_LANDING_PAGE:
        redirect_url = reverse("realm_redirect")
        if request.GET:
            redirect_url = add_query_to_redirect_url(redirect_url, request.GET.urlencode())
        return HttpResponseRedirect(redirect_url)

    realm = get_realm_from_request(request)
    if realm and realm.deactivated:
        return redirect_to_deactivation_notice()

    extra_context = kwargs.pop("extra_context", {})
    extra_context["next"] = next
    if dev_auth_enabled() and kwargs.get("template_name") == "zerver/development/dev_login.html":
        from zerver.views.development.dev_login import add_dev_login_context

        if "new_realm" in request.POST:
            try:
                realm = get_realm(request.POST["new_realm"])
            except Realm.DoesNotExist:
                realm = None

        add_dev_login_context(realm, extra_context)
        if realm and "new_realm" in request.POST:
            # If we're switching realms, redirect to that realm, but
            # only if it actually exists.
            return HttpResponseRedirect(realm.uri)

    if "username" in request.POST:
        extra_context["email"] = request.POST["username"]
    extra_context.update(login_context(request))

    if settings.TWO_FACTOR_AUTHENTICATION_ENABLED:
        return start_two_factor_auth(request, extra_context=extra_context, **kwargs)

    try:
        template_response = DjangoLoginView.as_view(
            authentication_form=OurAuthenticationForm, extra_context=extra_context, **kwargs
        )(request)
    except ZulipLDAPConfigurationError as e:
        assert len(e.args) > 1
        return redirect_to_misconfigured_ldap_notice(request, e.args[1])

    if isinstance(template_response, SimpleTemplateResponse):
        # Only those responses that are rendered using a template have
        # context_data attribute. This attribute doesn't exist otherwise. It is
        # added in SimpleTemplateResponse class, which is a derived class of
        # HttpResponse. See django.template.response.SimpleTemplateResponse,
        # https://github.com/django/django/blob/master/django/template/response.py#L19.
        update_login_page_context(request, template_response.context_data)

    return template_response
コード例 #14
0
def accounts_register(request: HttpRequest) -> HttpResponse:
    try:
        key = request.POST.get("key", default="")
        confirmation = Confirmation.objects.get(confirmation_key=key)
    except Confirmation.DoesNotExist:
        return render(request, "zerver/confirmation_link_expired_error.html")

    prereg_user = confirmation.content_object
    if prereg_user.status == confirmation_settings.STATUS_REVOKED:
        return render(request, "zerver/confirmation_link_expired_error.html")
    email = prereg_user.email
    realm_creation = prereg_user.realm_creation
    password_required = prereg_user.password_required

    role = prereg_user.invited_as
    if realm_creation:
        role = UserProfile.ROLE_REALM_OWNER

    try:
        validators.validate_email(email)
    except ValidationError:
        return render(request,
                      "zerver/invalid_email.html",
                      context={"invalid_email": True})

    if realm_creation:
        # For creating a new realm, there is no existing realm or domain
        realm = None
    else:
        if get_subdomain(request) != prereg_user.realm.string_id:
            return render_confirmation_key_error(
                request,
                ConfirmationKeyException(
                    ConfirmationKeyException.DOES_NOT_EXIST))
        realm = prereg_user.realm
        try:
            email_allowed_for_realm(email, realm)
        except DomainNotAllowedForRealmError:
            return render(
                request,
                "zerver/invalid_email.html",
                context={
                    "realm_name": realm.name,
                    "closed_domain": True
                },
            )
        except DisposableEmailError:
            return render(
                request,
                "zerver/invalid_email.html",
                context={
                    "realm_name": realm.name,
                    "disposable_emails_not_allowed": True
                },
            )
        except EmailContainsPlusError:
            return render(
                request,
                "zerver/invalid_email.html",
                context={
                    "realm_name": realm.name,
                    "email_contains_plus": True
                },
            )

        if realm.deactivated:
            # The user is trying to register for a deactivated realm. Advise them to
            # contact support.
            return redirect_to_deactivation_notice()

        try:
            validate_email_not_already_in_realm(realm, email)
        except ValidationError:
            return redirect_to_email_login_url(email)

    name_validated = False
    full_name = None
    require_ldap_password = False

    if request.POST.get("from_confirmation"):
        try:
            del request.session["authenticated_full_name"]
        except KeyError:
            pass

        ldap_full_name = None
        if settings.POPULATE_PROFILE_VIA_LDAP:
            # If the user can be found in LDAP, we'll take the full name from the directory,
            # and further down create a form pre-filled with it.
            for backend in get_backends():
                if isinstance(backend, LDAPBackend):
                    try:
                        ldap_username = backend.django_to_ldap_username(email)
                    except ZulipLDAPExceptionNoMatchingLDAPUser:
                        logging.warning(
                            "New account email %s could not be found in LDAP",
                            email)
                        break

                    # Note that this `ldap_user` object is not a
                    # `ZulipLDAPUser` with a `Realm` attached, so
                    # calling `.populate_user()` on it will crash.
                    # This is OK, since we're just accessing this user
                    # to extract its name.
                    #
                    # TODO: We should potentially be accessing this
                    # user to sync its initial avatar and custom
                    # profile fields as well, if we indeed end up
                    # creating a user account through this flow,
                    # rather than waiting until `manage.py
                    # sync_ldap_user_data` runs to populate it.
                    ldap_user = _LDAPUser(backend, ldap_username)

                    try:
                        ldap_full_name = backend.get_mapped_name(ldap_user)
                    except TypeError:
                        break

                    # Check whether this is ZulipLDAPAuthBackend,
                    # which is responsible for authentication and
                    # requires that LDAP accounts enter their LDAP
                    # password to register, or ZulipLDAPUserPopulator,
                    # which just populates UserProfile fields (no auth).
                    require_ldap_password = isinstance(backend,
                                                       ZulipLDAPAuthBackend)
                    break

        if ldap_full_name:
            # We don't use initial= here, because if the form is
            # complete (that is, no additional fields need to be
            # filled out by the user) we want the form to validate,
            # so they can be directly registered without having to
            # go through this interstitial.
            form = RegistrationForm({"full_name": ldap_full_name},
                                    realm_creation=realm_creation)
            request.session["authenticated_full_name"] = ldap_full_name
            name_validated = True
        elif realm is not None and realm.is_zephyr_mirror_realm:
            # For MIT users, we can get an authoritative name from Hesiod.
            # Technically we should check that this is actually an MIT
            # realm, but we can cross that bridge if we ever get a non-MIT
            # zephyr mirroring realm.
            hesiod_name = compute_mit_user_fullname(email)
            form = RegistrationForm(
                initial={
                    "full_name": hesiod_name if "@" not in hesiod_name else ""
                },
                realm_creation=realm_creation,
            )
            name_validated = True
        elif prereg_user.full_name:
            if prereg_user.full_name_validated:
                request.session[
                    "authenticated_full_name"] = prereg_user.full_name
                name_validated = True
                form = RegistrationForm({"full_name": prereg_user.full_name},
                                        realm_creation=realm_creation)
            else:
                form = RegistrationForm(
                    initial={"full_name": prereg_user.full_name},
                    realm_creation=realm_creation)
        elif "full_name" in request.POST:
            form = RegistrationForm(
                initial={"full_name": request.POST.get("full_name")},
                realm_creation=realm_creation,
            )
        else:
            form = RegistrationForm(realm_creation=realm_creation)
    else:
        postdata = request.POST.copy()
        if name_changes_disabled(realm):
            # If we populate profile information via LDAP and we have a
            # verified name from you on file, use that. Otherwise, fall
            # back to the full name in the request.
            try:
                postdata.update(
                    full_name=request.session["authenticated_full_name"])
                name_validated = True
            except KeyError:
                pass
        form = RegistrationForm(postdata, realm_creation=realm_creation)

    if not (password_auth_enabled(realm) and password_required):
        form["password"].field.required = False

    if form.is_valid():
        if password_auth_enabled(realm) and form["password"].field.required:
            password = form.cleaned_data["password"]
        else:
            # If the user wasn't prompted for a password when
            # completing the authentication form (because they're
            # signing up with SSO and no password is required), set
            # the password field to `None` (Which causes Django to
            # create an unusable password).
            password = None

        if realm_creation:
            string_id = form.cleaned_data["realm_subdomain"]
            realm_name = form.cleaned_data["realm_name"]
            realm = do_create_realm(string_id, realm_name)
            setup_realm_internal_bots(realm)
        assert realm is not None

        full_name = form.cleaned_data["full_name"]
        default_stream_group_names = request.POST.getlist(
            "default_stream_group")
        default_stream_groups = lookup_default_stream_groups(
            default_stream_group_names, realm)

        timezone = ""
        if "timezone" in request.POST and request.POST[
                "timezone"] in pytz.all_timezones_set:
            timezone = request.POST["timezone"]

        if "source_realm_id" in request.POST:
            # Non-integer realm_id values like "string" are treated
            # like the "Do not import" value of "".
            try:
                source_realm_id = int(request.POST["source_realm_id"])
            except ValueError:
                source_profile: Optional[UserProfile] = None
            else:
                source_profile = get_source_profile(email, source_realm_id)
        else:
            source_profile = None

        if not realm_creation:
            try:
                existing_user_profile: Optional[
                    UserProfile] = get_user_by_delivery_email(email, realm)
            except UserProfile.DoesNotExist:
                existing_user_profile = None
        else:
            existing_user_profile = None

        user_profile: Optional[UserProfile] = None
        return_data: Dict[str, bool] = {}
        if ldap_auth_enabled(realm):
            # If the user was authenticated using an external SSO
            # mechanism like Google or GitHub auth, then authentication
            # will have already been done before creating the
            # PreregistrationUser object with password_required=False, and
            # so we don't need to worry about passwords.
            #
            # If instead the realm is using EmailAuthBackend, we will
            # set their password above.
            #
            # But if the realm is using LDAPAuthBackend, we need to verify
            # their LDAP password (which will, as a side effect, create
            # the user account) here using authenticate.
            # pregeg_user.realm_creation carries the information about whether
            # we're in realm creation mode, and the ldap flow will handle
            # that and create the user with the appropriate parameters.
            user_profile = authenticate(
                request=request,
                username=email,
                password=password,
                realm=realm,
                prereg_user=prereg_user,
                return_data=return_data,
            )
            if user_profile is None:
                can_use_different_backend = email_auth_enabled(
                    realm) or any_social_backend_enabled(realm)
                if settings.LDAP_APPEND_DOMAIN:
                    # In LDAP_APPEND_DOMAIN configurations, we don't allow making a non-LDAP account
                    # if the email matches the ldap domain.
                    can_use_different_backend = can_use_different_backend and (
                        not email_belongs_to_ldap(realm, email))
                if return_data.get(
                        "no_matching_ldap_user") and can_use_different_backend:
                    # If both the LDAP and Email or Social auth backends are
                    # enabled, and there's no matching user in the LDAP
                    # directory then the intent is to create a user in the
                    # realm with their email outside the LDAP organization
                    # (with e.g. a password stored in the Zulip database,
                    # not LDAP).  So we fall through and create the new
                    # account.
                    pass
                else:
                    # TODO: This probably isn't going to give a
                    # user-friendly error message, but it doesn't
                    # particularly matter, because the registration form
                    # is hidden for most users.
                    view_url = reverse("login")
                    query = urlencode({"email": email})
                    redirect_url = add_query_to_redirect_url(view_url, query)
                    return HttpResponseRedirect(redirect_url)
            elif not realm_creation:
                # Since we'll have created a user, we now just log them in.
                return login_and_go_to_home(request, user_profile)
            else:
                # With realm_creation=True, we're going to return further down,
                # after finishing up the creation process.
                pass

        if existing_user_profile is not None and existing_user_profile.is_mirror_dummy:
            user_profile = existing_user_profile
            do_activate_user(user_profile, acting_user=user_profile)
            do_change_password(user_profile, password)
            do_change_full_name(user_profile, full_name, user_profile)
            do_set_user_display_setting(user_profile, "timezone", timezone)
            # TODO: When we clean up the `do_activate_user` code path,
            # make it respect invited_as_admin / is_realm_admin.

        if user_profile is None:
            user_profile = do_create_user(
                email,
                password,
                realm,
                full_name,
                prereg_user=prereg_user,
                role=role,
                tos_version=settings.TOS_VERSION,
                timezone=timezone,
                default_stream_groups=default_stream_groups,
                source_profile=source_profile,
                realm_creation=realm_creation,
                acting_user=None,
            )

        if realm_creation:
            bulk_add_subscriptions(realm, [realm.signup_notifications_stream],
                                   [user_profile],
                                   acting_user=None)
            send_initial_realm_messages(realm)

            # 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(
                ExternalAuthResult(user_profile=user_profile,
                                   data_dict={"is_realm_creation": True}))

        # This dummy_backend check below confirms the user is
        # authenticating to the correct subdomain.
        auth_result = authenticate(
            username=user_profile.delivery_email,
            realm=realm,
            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.delivery_email,
            )
            return redirect("/")

        return login_and_go_to_home(request, auth_result)

    return render(
        request,
        "zerver/register.html",
        context={
            "form":
            form,
            "email":
            email,
            "key":
            key,
            "full_name":
            request.session.get("authenticated_full_name", None),
            "lock_name":
            name_validated and name_changes_disabled(realm),
            # password_auth_enabled is normally set via our context processor,
            # but for the registration form, there is no logged in user yet, so
            # we have to set it here.
            "creating_new_team":
            realm_creation,
            "password_required":
            password_auth_enabled(realm) and password_required,
            "require_ldap_password":
            require_ldap_password,
            "password_auth_enabled":
            password_auth_enabled(realm),
            "root_domain_available":
            is_root_domain_available(),
            "default_stream_groups":
            [] if realm is None else get_default_stream_groups(realm),
            "accounts":
            get_accounts_for_email(email),
            "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),
        },
    )
コード例 #15
0
def redirect_to_email_login_url(email: str) -> HttpResponseRedirect:
    login_url = reverse('login')
    email = urllib.parse.quote_plus(email)
    redirect_url = add_query_to_redirect_url(login_url,
                                             'already_registered=' + email)
    return HttpResponseRedirect(redirect_url)
コード例 #16
0
ファイル: registration.py プロジェクト: pastewka/zulip
def find_account(
    request: HttpRequest,
    raw_emails: Optional[str] = REQ("emails", default=None)
) -> HttpResponse:
    url = reverse("find_account")

    emails: List[str] = []
    if request.method == "POST":
        form = FindMyTeamForm(request.POST)
        if form.is_valid():
            emails = form.cleaned_data["emails"]
            for i in range(len(emails)):
                try:
                    rate_limit_request_by_ip(request,
                                             domain="find_account_by_ip")
                except RateLimited as e:
                    assert e.secs_to_freedom is not None
                    return render(
                        request,
                        "zerver/rate_limit_exceeded.html",
                        context={"retry_after": int(e.secs_to_freedom)},
                        status=429,
                    )

            # Django doesn't support __iexact__in lookup with EmailField, so we have
            # to use Qs to get around that without needing to do multiple queries.
            emails_q = Q()
            for email in emails:
                emails_q |= Q(delivery_email__iexact=email)

            user_profiles = UserProfile.objects.filter(
                emails_q,
                is_active=True,
                is_bot=False,
                realm__deactivated=False)

            # We organize the data in preparation for sending exactly
            # one outgoing email per provided email address, with each
            # email listing all of the accounts that email address has
            # with the current Zulip server.
            context: Dict[str, Dict[str, Any]] = {}
            for user in user_profiles:
                key = user.delivery_email.lower()
                context.setdefault(key, {})
                context[key].setdefault("realms", [])
                context[key]["realms"].append(user.realm)
                context[key]["external_host"] = settings.EXTERNAL_HOST
                # This value will end up being the last user ID among
                # matching accounts; since it's only used for minor
                # details like language, that arbitrary choice is OK.
                context[key]["to_user_id"] = user.id

            for delivery_email, realm_context in context.items():
                realm_context["email"] = delivery_email
                send_email(
                    "zerver/emails/find_team",
                    to_user_ids=[realm_context["to_user_id"]],
                    context=realm_context,
                    from_address=FromAddress.SUPPORT,
                    request=request,
                )

            # Note: Show all the emails in the result otherwise this
            # feature can be used to ascertain which email addresses
            # are associated with Zulip.
            data = urllib.parse.urlencode({"emails": ",".join(emails)})
            return redirect(add_query_to_redirect_url(url, data))
    else:
        form = FindMyTeamForm()
        # The below validation is perhaps unnecessary, in that we
        # shouldn't get able to get here with an invalid email unless
        # the user hand-edits the URLs.
        if raw_emails:
            for email in raw_emails.split(","):
                try:
                    validators.validate_email(email)
                    emails.append(email)
                except ValidationError:
                    pass

    return render(
        request,
        "zerver/find_account.html",
        context={
            "form": form,
            "current_url": lambda: url,
            "emails": emails
        },
    )