mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 05:23:35 +00:00
remote_billing: Add flow for legacy servers.
This commit is contained in:
committed by
Tim Abbott
parent
d8cf12eaaa
commit
2765c63f56
@@ -18,6 +18,12 @@ class RemoteBillingIdentityDict(TypedDict):
|
|||||||
remote_realm_uuid: str
|
remote_realm_uuid: str
|
||||||
|
|
||||||
|
|
||||||
|
class LegacyServerIdentityDict(TypedDict):
|
||||||
|
# Currently this has only one field. We can extend this
|
||||||
|
# to add more information as appropriate.
|
||||||
|
remote_server_uuid: str
|
||||||
|
|
||||||
|
|
||||||
def get_identity_dict_from_session(
|
def get_identity_dict_from_session(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
realm_uuid: Optional[str],
|
realm_uuid: Optional[str],
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from corporate.views.portico import (
|
|||||||
team_view,
|
team_view,
|
||||||
)
|
)
|
||||||
from corporate.views.remote_billing_page import (
|
from corporate.views.remote_billing_page import (
|
||||||
|
remote_billing_legacy_server_login,
|
||||||
remote_billing_page_realm,
|
remote_billing_page_realm,
|
||||||
remote_billing_page_server,
|
remote_billing_page_server,
|
||||||
remote_billing_plans_realm,
|
remote_billing_plans_realm,
|
||||||
@@ -169,4 +170,9 @@ urlpatterns += [
|
|||||||
path("realm/<realm_uuid>/billing", remote_billing_page_realm, name="remote_billing_page_realm"),
|
path("realm/<realm_uuid>/billing", remote_billing_page_realm, name="remote_billing_page_realm"),
|
||||||
path("server/<server_uuid>/", remote_billing_page_server, name="remote_billing_page_server"),
|
path("server/<server_uuid>/", remote_billing_page_server, name="remote_billing_page_server"),
|
||||||
path("realm/<realm_uuid>/upgrade", remote_realm_upgrade_page, name="remote_realm_upgrade_page"),
|
path("realm/<realm_uuid>/upgrade", remote_realm_upgrade_page, name="remote_realm_upgrade_page"),
|
||||||
|
path(
|
||||||
|
"serverlogin/",
|
||||||
|
remote_billing_legacy_server_login,
|
||||||
|
name="remote_billing_legacy_server_login",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ from django.core import signing
|
|||||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.crypto import constant_time_compare
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from pydantic import Json
|
from pydantic import Json
|
||||||
|
|
||||||
from corporate.lib.decorator import self_hosting_management_endpoint
|
from corporate.lib.decorator import self_hosting_management_endpoint
|
||||||
from corporate.lib.remote_billing_util import (
|
from corporate.lib.remote_billing_util import (
|
||||||
|
LegacyServerIdentityDict,
|
||||||
RemoteBillingIdentityDict,
|
RemoteBillingIdentityDict,
|
||||||
get_identity_dict_from_session,
|
get_identity_dict_from_session,
|
||||||
)
|
)
|
||||||
@@ -19,7 +21,7 @@ from zerver.lib.exceptions import JsonableError, MissingRemoteRealmError
|
|||||||
from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling
|
from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.typed_endpoint import PathOnly, typed_endpoint
|
from zerver.lib.typed_endpoint import PathOnly, typed_endpoint
|
||||||
from zilencer.models import RemoteRealm, RemoteZulipServer
|
from zilencer.models import RemoteRealm, RemoteZulipServer, get_remote_server_by_uuid
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
@@ -96,36 +98,46 @@ def render_tmp_remote_billing_page(
|
|||||||
if identity_dict is None:
|
if identity_dict is None:
|
||||||
raise JsonableError(_("User not authenticated"))
|
raise JsonableError(_("User not authenticated"))
|
||||||
|
|
||||||
user_email = identity_dict["user_email"]
|
# This key should be set in both RemoteRealm and legacy server
|
||||||
user_full_name = identity_dict["user_full_name"]
|
# login flows.
|
||||||
remote_server_uuid = identity_dict["remote_server_uuid"]
|
remote_server_uuid = identity_dict["remote_server_uuid"]
|
||||||
remote_realm_uuid = identity_dict["remote_realm_uuid"]
|
user_email = identity_dict.get("user_email")
|
||||||
|
user_full_name = identity_dict.get("user_full_name")
|
||||||
|
remote_realm_uuid = identity_dict.get("remote_realm_uuid")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
remote_server = RemoteZulipServer.objects.get(uuid=remote_server_uuid)
|
remote_server = RemoteZulipServer.objects.get(uuid=remote_server_uuid)
|
||||||
except RemoteZulipServer.DoesNotExist:
|
except RemoteZulipServer.DoesNotExist:
|
||||||
raise JsonableError(_("Invalid remote server."))
|
raise JsonableError(_("Invalid remote server."))
|
||||||
|
|
||||||
try:
|
remote_realm = None
|
||||||
# Checking for the (uuid, server) is sufficient to be secure here, since the server
|
if remote_realm_uuid:
|
||||||
# is authenticated. the uuid_owner_secret is not needed here, it'll be used for
|
try:
|
||||||
# for validating transfers of a realm to a different RemoteZulipServer (in the
|
# Checking for the (uuid, server) is sufficient to be secure here, since the server
|
||||||
# export-import process).
|
# is authenticated. the uuid_owner_secret is not needed here, it'll be used for
|
||||||
remote_realm = RemoteRealm.objects.get(uuid=remote_realm_uuid, server=remote_server)
|
# for validating transfers of a realm to a different RemoteZulipServer (in the
|
||||||
except RemoteRealm.DoesNotExist:
|
# export-import process).
|
||||||
raise AssertionError(
|
remote_realm = RemoteRealm.objects.get(uuid=remote_realm_uuid, server=remote_server)
|
||||||
"The remote realm is missing despite being in the RemoteBillingIdentityDict"
|
except RemoteRealm.DoesNotExist:
|
||||||
)
|
raise AssertionError(
|
||||||
|
"The remote realm is missing despite being in the RemoteBillingIdentityDict"
|
||||||
|
)
|
||||||
|
|
||||||
remote_server_and_realm_info = {
|
remote_server_and_realm_info = {
|
||||||
"remote_server_uuid": remote_server_uuid,
|
"remote_server_uuid": remote_server_uuid,
|
||||||
"remote_server_hostname": remote_server.hostname,
|
"remote_server_hostname": remote_server.hostname,
|
||||||
"remote_server_contact_email": remote_server.contact_email,
|
"remote_server_contact_email": remote_server.contact_email,
|
||||||
"remote_server_plan_type": remote_server.plan_type,
|
"remote_server_plan_type": remote_server.plan_type,
|
||||||
"remote_realm_uuid": remote_realm_uuid,
|
|
||||||
"remote_realm_host": remote_realm.host,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if remote_realm is not None:
|
||||||
|
remote_server_and_realm_info.update(
|
||||||
|
{
|
||||||
|
"remote_realm_uuid": remote_realm_uuid,
|
||||||
|
"remote_realm_host": remote_realm.host,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"corporate/remote_billing.html",
|
"corporate/remote_billing.html",
|
||||||
@@ -185,3 +197,54 @@ def remote_billing_page_server(request: HttpRequest, *, server_uuid: PathOnly[st
|
|||||||
@typed_endpoint
|
@typed_endpoint
|
||||||
def remote_billing_page_realm(request: HttpRequest, *, realm_uuid: PathOnly[str]) -> HttpResponse:
|
def remote_billing_page_realm(request: HttpRequest, *, realm_uuid: PathOnly[str]) -> HttpResponse:
|
||||||
return remote_billing_page_common(request, realm_uuid=realm_uuid, server_uuid=None)
|
return remote_billing_page_common(request, realm_uuid=realm_uuid, server_uuid=None)
|
||||||
|
|
||||||
|
|
||||||
|
@self_hosting_management_endpoint
|
||||||
|
@typed_endpoint
|
||||||
|
def remote_billing_legacy_server_login(
|
||||||
|
request: HttpRequest,
|
||||||
|
*,
|
||||||
|
server_org_id: Optional[str] = None,
|
||||||
|
server_org_secret: Optional[str] = None,
|
||||||
|
) -> HttpResponse:
|
||||||
|
if server_org_id is None or server_org_secret is None:
|
||||||
|
# Should not be possible to submit the form like this, so this is the default
|
||||||
|
# case, where the user just opened this page and therefore we render a fresh form.
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"corporate/legacy_server_login.html",
|
||||||
|
context={"error_message": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
remote_server = get_remote_server_by_uuid(server_org_id)
|
||||||
|
except RemoteZulipServer.DoesNotExist:
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"corporate/legacy_server_login.html",
|
||||||
|
context={
|
||||||
|
"error_message": _("Did not find a server registration for this server_org_id.")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if not constant_time_compare(server_org_secret, remote_server.api_key):
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"corporate/legacy_server_login.html",
|
||||||
|
context={"error_message": _("Invalid server_org_secret.")},
|
||||||
|
)
|
||||||
|
if remote_server.deactivated:
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"corporate/legacy_server_login.html",
|
||||||
|
context={"error_message": _("Your server registration has been deactivated.")},
|
||||||
|
)
|
||||||
|
|
||||||
|
remote_server_uuid = str(remote_server.uuid)
|
||||||
|
|
||||||
|
request.session["remote_billing_identities"] = {}
|
||||||
|
request.session["remote_billing_identities"][remote_server_uuid] = LegacyServerIdentityDict(
|
||||||
|
remote_server_uuid=remote_server_uuid
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse("remote_billing_page_server", args=(remote_server_uuid,)))
|
||||||
|
|||||||
45
templates/corporate/legacy_server_login.html
Normal file
45
templates/corporate/legacy_server_login.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{% extends "zerver/portico.html" %}
|
||||||
|
{% set entrypoint = "upgrade" %}
|
||||||
|
|
||||||
|
{% set PAGE_TITLE = "Zulip server billing management" %}
|
||||||
|
|
||||||
|
{% block portico_content %}
|
||||||
|
|
||||||
|
<div class="register-account flex full-page server-login-page">
|
||||||
|
<div class="center-block new-style">
|
||||||
|
<div class="pitch">
|
||||||
|
<h1>Zulip server billing management</h1>
|
||||||
|
</div>
|
||||||
|
<div class="white-box">
|
||||||
|
{% if error_message %}
|
||||||
|
<div id="server-login-error" class="alert alert-danger">{{ error_message }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div id="server-login-input-section">
|
||||||
|
<form id="server-login-form" method="post" action="/serverlogin/">
|
||||||
|
{{ csrf_input }}
|
||||||
|
<div class="input-box server-login-form-field">
|
||||||
|
<label for="username" class="inline-block label-title">
|
||||||
|
server_org_id
|
||||||
|
<a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html" target="_blank">
|
||||||
|
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</label>
|
||||||
|
<input id="username" name="server_org_id" class="required" type="text"/>
|
||||||
|
</div>
|
||||||
|
<div class="input-box server-login-form-field">
|
||||||
|
<label for="password" class="inline-block label-title">server_org_key</label>
|
||||||
|
<input id="password" name="server_org_secret" class="required" type="password"/>
|
||||||
|
</div>
|
||||||
|
<div class="upgrade-button-container">
|
||||||
|
<button type="submit" id="server-login-button" class="stripe-button-el invoice-button">
|
||||||
|
<span class="server-login-button-text">Login</span>
|
||||||
|
<img class="loader server-login-button-loader" src="{{ static('images/loading/loader-white.svg') }}" alt="" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
29
web/src/billing/legacy_server_login.ts
Normal file
29
web/src/billing/legacy_server_login.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
export function initialize(): void {
|
||||||
|
$("#server-login-form").validate({
|
||||||
|
errorClass: "text-error",
|
||||||
|
wrapper: "div",
|
||||||
|
submitHandler(form) {
|
||||||
|
$("#server-login-form").find(".loader").css("display", "inline-block");
|
||||||
|
$("#server-login-button .server-login-button-text").hide();
|
||||||
|
|
||||||
|
form.submit();
|
||||||
|
},
|
||||||
|
invalidHandler() {
|
||||||
|
// this removes all previous errors that were put on screen
|
||||||
|
// by the server.
|
||||||
|
$("#server-login-form .alert.alert-error").remove();
|
||||||
|
},
|
||||||
|
showErrors(error_map) {
|
||||||
|
if (error_map.password) {
|
||||||
|
$("#server-login-form .alert.alert-error").remove();
|
||||||
|
}
|
||||||
|
this.defaultShowErrors!();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
initialize();
|
||||||
|
});
|
||||||
@@ -656,3 +656,16 @@ input[name="licenses"] {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.server-login-button-loader {
|
||||||
|
display: none;
|
||||||
|
vertical-align: top;
|
||||||
|
position: relative;
|
||||||
|
height: 30px;
|
||||||
|
margin-top: -10px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#server-login-error {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
"./src/portico/tippyjs",
|
"./src/portico/tippyjs",
|
||||||
"./src/billing/helpers",
|
"./src/billing/helpers",
|
||||||
"./src/billing/upgrade",
|
"./src/billing/upgrade",
|
||||||
|
"jquery-validation",
|
||||||
|
"./src/billing/legacy_server_login",
|
||||||
"./styles/portico/billing.css"
|
"./styles/portico/billing.css"
|
||||||
],
|
],
|
||||||
"billing-event-status": [
|
"billing-event-status": [
|
||||||
|
|||||||
Reference in New Issue
Block a user