diff --git a/templates/zilencer/billing.html b/templates/zilencer/billing.html index 8c69cbd3e9..2596b7ff10 100644 --- a/templates/zilencer/billing.html +++ b/templates/zilencer/billing.html @@ -12,23 +12,27 @@

{{ _("Billing") }}

- Plan
- {{ plan_name }}
-
- You are paying for {{ seat_count }} users.
- Your plan will renew on {{ renewal_date }} for ${{ renewal_amount }}.
- {% if prorated_charges %} - You have ${{ prorated_charges }} in prorated charges that will be - added to your next bill. - {% elif prorated_credits %} - You have ${{ prorated_credits }} in prorated credits that will be - automatically applied to your next bill. + {% if admin_access %} + Plan
+ {{ plan_name }}
+
+ You are paying for {{ seat_count }} users.
+ Your plan will renew on {{ renewal_date }} for ${{ renewal_amount }}.
+ {% if prorated_charges %} + You have ${{ prorated_charges }} in prorated charges that will be + added to your next bill. + {% elif prorated_credits %} + You have ${{ prorated_credits }} in prorated credits that will be + automatically applied to your next bill. + {% endif %} +
+
+ Payment method: {{ payment_method }}.
+
+ Contact support@zulipchat.com for billing history or to make changes to your subscription. + {% else %} + You must be an organization administrator or a billing administrator to view this page. {% endif %} -
-
- Payment method: {{ payment_method }}.
-
- Contact support@zulipchat.com for billing history or to make changes to your subscription.
{% endblock %} diff --git a/zilencer/tests/test_stripe.py b/zilencer/tests/test_stripe.py index d3b896fbac..f2d35ff554 100644 --- a/zilencer/tests/test_stripe.py +++ b/zilencer/tests/test_stripe.py @@ -129,6 +129,37 @@ class StripeTest(ZulipTestCase): self.assertEqual(response.status_code, 302) self.assertEqual('/billing/', response.url) + @mock.patch("zilencer.lib.stripe.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key") + @mock.patch("zilencer.views.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key") + @mock.patch("stripe.Invoice.upcoming", side_effect=mock_upcoming_invoice) + @mock.patch("stripe.Customer.retrieve", side_effect=mock_retrieve_customer) + @mock.patch("stripe.Customer.create", side_effect=mock_create_customer) + @mock.patch("stripe.Subscription.create", side_effect=mock_create_subscription) + def test_billing_page_permissions(self, mock_create_subscription: mock.Mock, + mock_create_customer: mock.Mock, + mock_retrieve_customer: mock.Mock, + mock_upcoming_invoice: mock.Mock) -> None: + # Check that non-admins can access /upgrade via /billing, when there is no Customer object + self.login(self.example_email('hamlet')) + response = self.client_get("/billing/") + self.assertEqual(response.status_code, 302) + self.assertEqual('/upgrade/', response.url) + # Check that non-admins can sign up and pay + self.client_post("/upgrade/", {'stripeToken': self.token, + 'seat_count': self.quantity, + 'plan': Plan.CLOUD_ANNUAL}) + # Check that the non-admin hamlet can still access /billing + response = self.client_get("/billing/") + self.assert_in_success_response(["Contact support@zulipchat.com for billing"], response) + # Check admins can access billing, even though they are not the billing_user + self.login(self.example_email('iago')) + response = self.client_get("/billing/") + self.assert_in_success_response(["Contact support@zulipchat.com for billing"], response) + # Check that non-admin, non-billing_user does not have access + self.login(self.example_email("cordelia")) + response = self.client_get("/billing/") + self.assert_in_success_response(["You must be an organization administrator"], response) + @mock.patch("zilencer.lib.stripe.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key") @mock.patch("zilencer.views.STRIPE_PUBLISHABLE_KEY", "stripe_publishable_key") @mock.patch("stripe.Customer.create", side_effect=mock_create_customer) diff --git a/zilencer/views.py b/zilencer/views.py index fe0cf97039..49c1d52962 100644 --- a/zilencer/views.py +++ b/zilencer/views.py @@ -199,10 +199,10 @@ def billing_home(request: HttpRequest) -> HttpResponse: if customer is None: return HttpResponseRedirect(reverse('zilencer.views.initial_upgrade')) - # TODO - # if not user.is_realm_admin and not user == customer.billing_user: - # context['error_message'] = _("You must be an administrator to view this page.") - # return render(request, 'zilencer/billing.html', context=context) + if not user.is_realm_admin and not user == customer.billing_user: + context = {'admin_access': False} # type: Dict[str, Any] + return render(request, 'zilencer/billing.html', context=context) + context = {'admin_access': True} stripe_customer = get_stripe_customer(customer.stripe_customer_id) @@ -230,7 +230,7 @@ def billing_home(request: HttpRequest) -> HttpResponse: if source is not None: payment_method = "Card ending in %(last4)s" % {'last4': source.last4} - context = { + context.update({ 'plan_name': plan_name, 'seat_count': seat_count, 'renewal_date': renewal_date, @@ -238,6 +238,6 @@ def billing_home(request: HttpRequest) -> HttpResponse: 'payment_method': payment_method, 'prorated_charges': '{:,.2f}'.format(prorated_charges), 'prorated_credits': '{:,.2f}'.format(prorated_credits), - } # type: Dict[str, Any] + }) return render(request, 'zilencer/billing.html', context=context)