diff --git a/cloud/__init__.py b/cloud/__init__.py index d2e1367..13abe92 100644 --- a/cloud/__init__.py +++ b/cloud/__init__.py @@ -38,7 +38,10 @@ class CloudExtension(PillarExtension): # Just so that it registers the management commands. from . import cli - return {} + return { + 'EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER': 'https://store.blender.org/api/', + 'EXTERNAL_SUBSCRIPTIONS_TIMEOUT_SECS': 10, + } def eve_settings(self): """Returns extensions to the Eve settings. diff --git a/cloud/routes.py b/cloud/routes.py index f23bdd9..5006896 100644 --- a/cloud/routes.py +++ b/cloud/routes.py @@ -340,6 +340,8 @@ def emails(): def billing(): """View the subscription status of a user """ + from . import store + if current_user.has_role('protected'): return abort(404) # TODO: make this 403, handle template properly api = system_util.pillar_api() @@ -350,7 +352,7 @@ def billing(): group = Group.find(group_id, api=api) groups.append(group.name) - store_user = pillar.api.blender_cloud.subscription.fetch_subscription_info(user.email) + store_user = store.fetch_subscription_info(user.email) return render_template( 'users/settings/billing.html', diff --git a/cloud/store.py b/cloud/store.py new file mode 100644 index 0000000..ca4750d --- /dev/null +++ b/cloud/store.py @@ -0,0 +1,72 @@ +"""Blender Store interface.""" + +import logging +import typing + +from pillar import current_app + +log = logging.getLogger(__name__) + + +def fetch_subscription_info(email: str) -> typing.Optional[dict]: + """Returns the user info dict from the external subscriptions management server. + + :returns: the store user info, or None if the user can't be found or there + was an error communicating. A dict like this is returned: + { + "shop_id": 700, + "cloud_access": 1, + "paid_balance": 314.75, + "balance_currency": "EUR", + "start_date": "2014-08-25 17:05:46", + "expiration_date": "2016-08-24 13:38:45", + "subscription_status": "wc-active", + "expiration_date_approximate": true + } + """ + + from requests.adapters import HTTPAdapter + import requests.exceptions + + external_subscriptions_server = current_app.config['EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER'] + + if log.isEnabledFor(logging.DEBUG): + import urllib.parse + + log_email = urllib.parse.quote(email) + log.debug('Connecting to store at %s?blenderid=%s', + external_subscriptions_server, log_email) + + # Retry a few times when contacting the store. + s = requests.Session() + s.mount(external_subscriptions_server, HTTPAdapter(max_retries=5)) + + try: + r = s.get(external_subscriptions_server, + params={'blenderid': email}, + verify=current_app.config['TLS_CERT_FILE'], + timeout=current_app.config.get('EXTERNAL_SUBSCRIPTIONS_TIMEOUT_SECS', 10)) + except requests.exceptions.ConnectionError as ex: + log.error('Error connecting to %s: %s', external_subscriptions_server, ex) + return None + except requests.exceptions.Timeout as ex: + log.error('Timeout communicating with %s: %s', external_subscriptions_server, ex) + return None + except requests.exceptions.RequestException as ex: + log.error('Some error communicating with %s: %s', external_subscriptions_server, ex) + return None + + if r.status_code != 200: + log.warning("Error communicating with %s, code=%i, unable to check " + "subscription status of user %s", + external_subscriptions_server, r.status_code, email) + return None + + store_user = r.json() + + if log.isEnabledFor(logging.DEBUG): + import json + log.debug('Received JSON from store API: %s', + json.dumps(store_user, sort_keys=False, indent=4)) + + return store_user diff --git a/src/templates/users/settings/billing.pug b/src/templates/users/settings/billing.pug index 1cb0069..e1e5959 100644 --- a/src/templates/users/settings/billing.pug +++ b/src/templates/users/settings/billing.pug @@ -15,7 +15,7 @@ style(type='text/css'). .settings-content - | {% if store_user['cloud_access'] %} + | {% if current_user.has_role('subscriber') %} h3.subscription-active i.pi-check | Your subscription is active