mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 03:53:50 +00:00 
			
		
		
		
	remote_billing: Add redirects to login for unauthed user in legacy flow.
Analogical to the more complex mechanism implemented for the RemoteRealm flow in a previous commit in authenticated_remote_realm_management_endpoint. As explained in the code comment, this is much easier because: In this flow, we can only redirect to our local "legacy server flow login" page. That means that we can do it universally whether the user has an expired identity_dict, or just lacks any form of authentication info at all - there are no security concerns since this is just a local redirect.
This commit is contained in:
		
				
					committed by
					
						 Tim Abbott
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							44ac99b8fc
						
					
				
				
					commit
					134e3bfa68
				
			| @@ -1,10 +1,11 @@ | ||||
| from functools import wraps | ||||
| from typing import Callable | ||||
| from typing import Callable, Optional | ||||
| from urllib.parse import urlencode, urljoin | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.http import HttpRequest, HttpResponse, HttpResponseRedirect | ||||
| from django.shortcuts import render | ||||
| from django.urls import reverse | ||||
| from typing_extensions import Concatenate, ParamSpec | ||||
|  | ||||
| from corporate.lib.remote_billing_util import ( | ||||
| @@ -13,6 +14,7 @@ from corporate.lib.remote_billing_util import ( | ||||
|     get_remote_server_from_session, | ||||
| ) | ||||
| from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession | ||||
| from zerver.lib.exceptions import RemoteBillingAuthenticationError | ||||
| from zerver.lib.subdomains import get_subdomain | ||||
| from zerver.lib.url_encoding import append_url_query_string | ||||
| from zilencer.models import RemoteRealm | ||||
| @@ -95,22 +97,8 @@ def authenticated_remote_realm_management_endpoint( | ||||
|             # these redirects to work there for testing. | ||||
|             url = urljoin(uri_scheme + remote_realm.host, "/self-hosted-billing/") | ||||
|  | ||||
|             # Our endpoint URLs in this subsystem end with something like | ||||
|             # /sponsorship or /plans etc. | ||||
|             # Therefore we can use this nice property to figure out easily what | ||||
|             # kind of page the user is trying to access and find the right value | ||||
|             # for the `next` query parameter. | ||||
|             path = request.path | ||||
|             if path.endswith("/"):  # nocoverage | ||||
|                 path = path[:-1] | ||||
|  | ||||
|             page_type = path.split("/")[-1] | ||||
|  | ||||
|             from corporate.views.remote_billing_page import ( | ||||
|                 VALID_NEXT_PAGES as REMOTE_BILLING_VALID_NEXT_PAGES, | ||||
|             ) | ||||
|  | ||||
|             if page_type in REMOTE_BILLING_VALID_NEXT_PAGES: | ||||
|             page_type = get_next_page_param_from_request_path(request) | ||||
|             if page_type is not None: | ||||
|                 query = urlencode({"next_page": page_type}) | ||||
|                 url = append_url_query_string(url, query) | ||||
|  | ||||
| @@ -122,6 +110,31 @@ def authenticated_remote_realm_management_endpoint( | ||||
|     return _wrapped_view_func | ||||
|  | ||||
|  | ||||
| def get_next_page_param_from_request_path(request: HttpRequest) -> Optional[str]:  # nocoverage | ||||
|     # Our endpoint URLs in this subsystem end with something like | ||||
|     # /sponsorship or /plans etc. | ||||
|     # Therefore we can use this nice property to figure out easily what | ||||
|     # kind of page the user is trying to access and find the right value | ||||
|     # for the `next` query parameter. | ||||
|     path = request.path | ||||
|     if path.endswith("/"): | ||||
|         path = path[:-1] | ||||
|  | ||||
|     page_type = path.split("/")[-1] | ||||
|  | ||||
|     from corporate.views.remote_billing_page import ( | ||||
|         VALID_NEXT_PAGES as REMOTE_BILLING_VALID_NEXT_PAGES, | ||||
|     ) | ||||
|  | ||||
|     if page_type in REMOTE_BILLING_VALID_NEXT_PAGES: | ||||
|         return page_type | ||||
|  | ||||
|     # Should be impossible to reach here. If this is reached, it must mean | ||||
|     # we have a registered endpoint that doesn't have a VALID_NEXT_PAGES entry | ||||
|     # or the parsing logic above is failing. | ||||
|     raise AssertionError(f"Unknown page type: {page_type}") | ||||
|  | ||||
|  | ||||
| def authenticated_remote_server_management_endpoint( | ||||
|     view_func: Callable[Concatenate[HttpRequest, RemoteServerBillingSession, ParamT], HttpResponse] | ||||
| ) -> Callable[Concatenate[HttpRequest, ParamT], HttpResponse]:  # nocoverage | ||||
| @@ -139,7 +152,21 @@ def authenticated_remote_server_management_endpoint( | ||||
|         if not isinstance(server_uuid, str): | ||||
|             raise TypeError("server_uuid must be a string") | ||||
|  | ||||
|         remote_server = get_remote_server_from_session(request, server_uuid=server_uuid) | ||||
|         try: | ||||
|             remote_server = get_remote_server_from_session(request, server_uuid=server_uuid) | ||||
|         except (RemoteBillingIdentityExpiredError, RemoteBillingAuthenticationError): | ||||
|             # In this flow, we can only redirect to our local "legacy server flow login" page. | ||||
|             # That means that we can do it universally whether the user has an expired | ||||
|             # identity_dict, or just lacks any form of authentication info at all - there | ||||
|             # are no security concerns since this is just a local redirect. | ||||
|             url = reverse("remote_billing_legacy_server_login") | ||||
|             page_type = get_next_page_param_from_request_path(request) | ||||
|             if page_type is not None: | ||||
|                 query = urlencode({"next_page": page_type}) | ||||
|                 url = append_url_query_string(url, query) | ||||
|  | ||||
|             return HttpResponseRedirect(url) | ||||
|  | ||||
|         billing_session = RemoteServerBillingSession(remote_server) | ||||
|         return view_func(request, billing_session) | ||||
|  | ||||
|   | ||||
| @@ -137,7 +137,7 @@ def get_remote_server_from_session( | ||||
|     ) | ||||
|  | ||||
|     if identity_dict is None: | ||||
|         raise JsonableError(_("User not authenticated")) | ||||
|         raise RemoteBillingAuthenticationError | ||||
|  | ||||
|     remote_server_uuid = identity_dict["remote_server_uuid"] | ||||
|     try: | ||||
|   | ||||
| @@ -14,6 +14,7 @@ from pydantic import Json | ||||
|  | ||||
| from corporate.lib.decorator import ( | ||||
|     authenticated_remote_realm_management_endpoint, | ||||
|     authenticated_remote_server_management_endpoint, | ||||
|     self_hosting_management_endpoint, | ||||
| ) | ||||
| from corporate.lib.remote_billing_util import ( | ||||
| @@ -23,12 +24,12 @@ from corporate.lib.remote_billing_util import ( | ||||
|     RemoteBillingUserDict, | ||||
|     get_identity_dict_from_session, | ||||
| ) | ||||
| from corporate.lib.stripe import RemoteRealmBillingSession | ||||
| from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession | ||||
| from zerver.lib.exceptions import JsonableError, MissingRemoteRealmError | ||||
| from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling | ||||
| from zerver.lib.response import json_success | ||||
| from zerver.lib.timestamp import datetime_to_timestamp | ||||
| from zerver.lib.typed_endpoint import PathOnly, typed_endpoint | ||||
| from zerver.lib.typed_endpoint import typed_endpoint | ||||
| from zilencer.models import RemoteRealm, RemoteZulipServer, get_remote_server_by_uuid | ||||
|  | ||||
| billing_logger = logging.getLogger("corporate.stripe") | ||||
| @@ -208,9 +209,11 @@ def remote_realm_plans_page( | ||||
|     return remote_billing_plans_common(request, realm_uuid=realm_uuid, server_uuid=None) | ||||
|  | ||||
|  | ||||
| @self_hosting_management_endpoint | ||||
| @typed_endpoint | ||||
| def remote_server_plans_page(request: HttpRequest, *, server_uuid: PathOnly[str]) -> HttpResponse: | ||||
| @authenticated_remote_server_management_endpoint | ||||
| def remote_server_plans_page( | ||||
|     request: HttpRequest, billing_session: RemoteServerBillingSession | ||||
| ) -> HttpResponse: | ||||
|     server_uuid = str(billing_session.remote_server.uuid) | ||||
|     return remote_billing_plans_common(request, server_uuid=server_uuid, realm_uuid=None) | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user