Check subscription status on login.

This commit is contained in:
Sybren A. Stüvel 2016-08-23 16:09:47 +02:00
parent dcbefc33ae
commit 07691db874
4 changed files with 113 additions and 9 deletions

View File

@ -64,7 +64,7 @@ class PillarServer(Eve):
self.load_config()
# Configure authentication
self._login_manager = auth.config_login_manager(self)
self.login_manager = auth.config_login_manager(self)
self.oauth_blender_id = auth.config_oauth_login(self)
self._config_caching()

View File

@ -0,0 +1,51 @@
"""Cloud subscription info.
Connects to the external subscription server to obtain user info.
"""
import logging
from flask import current_app
import requests
from requests.adapters import HTTPAdapter
log = logging.getLogger(__name__)
def fetch_user(email):
"""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
}
:rtype: dict
"""
external_subscriptions_server = current_app.config['EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER']
log.debug('Connecting to store at %s?blenderid=%s', external_subscriptions_server, email)
# Retry a few times when contacting the store.
s = requests.Session()
s.mount(external_subscriptions_server, HTTPAdapter(max_retries=5))
r = s.get(external_subscriptions_server, params={'blenderid': email},
verify=current_app.config['TLS_CERT_FILE'])
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()
return store_user

View File

@ -2,6 +2,8 @@ import os.path
from os import getenv
from collections import defaultdict
import requests.certs
RFC1123_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
PILLAR_SERVER_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -137,3 +139,6 @@ URLER_SERVICE_AUTH_TOKEN = None
BLENDER_CLOUD_ADDON_VERSION = '1.4'
EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER = 'https://store.blender.org/api/'
# Certificate file for communication with other systems.
TLS_CERT_FILE = requests.certs.where()

View File

@ -6,7 +6,7 @@ from flask import (abort, Blueprint, current_app, flash, redirect,
render_template, request, session, url_for)
from flask_login import login_required, login_user, logout_user, current_user
from flask_oauthlib.client import OAuthException
from pillar.auth import UserClass
from pillar.auth import UserClass, subscriptions
from pillar.web import system_util
from .forms import UserProfileForm
from .forms import UserSettingsEmailsForm
@ -55,13 +55,12 @@ def blender_id_authorized():
user = UserClass(oauth_resp['access_token'])
login_user(user)
# user = load_user(current_user.id)
current_app.login_manager.reload_user() # This ensures that flask_login.current_user is set.
if user is not None:
pass
if current_user is not None:
# Check with the store for user roles. If the user has an active
# subscription, we apply the 'subscriber' role
# user_roles_update(user.objectid)
user_roles_update(current_user.objectid)
next_after_login = session.get('next_after_login')
if next_after_login:
@ -179,9 +178,9 @@ def settings_billing():
for group_id in user.groups:
group = Group.find(group_id, api=api)
groups.append(group.name)
external_subscriptions_server = current_app.config['EXTERNAL_SUBSCRIPTIONS_MANAGEMENT_SERVER']
r = requests.get(external_subscriptions_server, params={'blenderid': user.email})
store_user = r.json()
store_user = subscriptions.fetch_user(user.email)
return render_template(
'users/settings/billing.html',
store_user=store_user, groups=groups, title='billing')
@ -237,3 +236,52 @@ def users_index():
if not current_user.has_role('admin'):
return abort(403)
return render_template('users/index.html')
def user_roles_update(user_id):
api = system_util.pillar_api()
group_subscriber = Group.find_one({'where': {'name': 'subscriber'}}, api=api)
# Fetch the user once outside the loop, because we only need to get the
# subscription status once.
user = User.me(api=api)
store_user = subscriptions.fetch_user(user.email)
if store_user is None:
return
max_retry = 5
for retry_count in range(max_retry):
# Update the user's role & groups for their subscription status.
roles = set(user.roles or [])
groups = set(user.groups or [])
if store_user['cloud_access'] == 1:
roles.add(u'subscriber')
groups.add(group_subscriber._id)
elif u'admin' not in roles:
roles.discard(u'subscriber')
groups.discard(group_subscriber._id)
# Only send an API request when the user has actually changed
if set(user.roles or []) == roles and set(user.groups or []) == groups:
break
user.roles = list(roles)
user.groups = list(groups)
try:
user.update(api=api)
except sdk_exceptions.PreconditionFailed:
log.warning('User etag changed while updating roles, retrying.')
else:
# Successful update, so we can stop the loop.
break
# Fetch the user for the next iteration.
if retry_count < max_retry - 1:
user = User.me(api=api)
else:
log.warning('Tried %i times to update user %s, and failed each time. Giving up.',
max_retry, user_id)