Added subclient token verification & storage.
This commit is contained in:
@@ -380,5 +380,7 @@ file_storage.setup_app(app, url_prefix='/storage')
|
||||
|
||||
# The encoding module (receive notification and report progress)
|
||||
from modules.encoding import encoding
|
||||
from modules.blender_id import blender_id
|
||||
|
||||
app.register_blueprint(encoding, url_prefix='/encoding')
|
||||
app.register_blueprint(blender_id, url_prefix='/blender_id')
|
||||
|
106
pillar/application/modules/blender_id.py
Normal file
106
pillar/application/modules/blender_id.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Blender ID subclient endpoint."""
|
||||
|
||||
import logging
|
||||
from pprint import pformat
|
||||
|
||||
import requests
|
||||
from flask import Blueprint, request, current_app, abort
|
||||
from eve.methods.post import post_internal
|
||||
from eve.methods.put import put_internal
|
||||
|
||||
from application.utils import authentication, remove_private_keys
|
||||
|
||||
blender_id = Blueprint('blender_id', __name__)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@blender_id.route('/store_scst', methods=['POST'])
|
||||
def store_subclient_token():
|
||||
"""Verifies & stores a user's subclient-specific token."""
|
||||
|
||||
user_id = request.form['user_id']
|
||||
scst = request.form['scst']
|
||||
|
||||
# Verify with Blender ID
|
||||
log.debug('Storing SCST for BlenderID user %s', user_id)
|
||||
user_info = validate_subclient_token(user_id, scst)
|
||||
|
||||
if user_info is None:
|
||||
log.warning('Unable to verify subclient token with Blender ID.')
|
||||
return 'BLENDER ID ERROR', 403
|
||||
|
||||
# Store the user info in MongoDB.
|
||||
log.info('Obtained user info from Blender ID: %s', user_info)
|
||||
db_user = find_user_in_db(user_id, scst, **user_info)
|
||||
log.debug('Storing updated/created user:\n%s', pformat(db_user))
|
||||
|
||||
if '_id' in db_user:
|
||||
db_id = db_user['_id']
|
||||
r, _, _, status = put_internal('users', remove_private_keys(db_user), _id=db_id)
|
||||
else:
|
||||
r, _, _, status = post_internal('users', db_user)
|
||||
if status != 200:
|
||||
log.error('internal response: %r %r', status, r)
|
||||
return abort(500)
|
||||
|
||||
return 'OK', 200
|
||||
|
||||
|
||||
def validate_subclient_token(user_id, scst):
|
||||
"""Verifies a subclient token with Blender ID.
|
||||
|
||||
:returns: the user information from Blender ID on success, in a dict
|
||||
{'email': 'a@b', 'full_name': 'AB'}, or None on failure.
|
||||
:rtype: dict
|
||||
"""
|
||||
|
||||
client_id = current_app.config['BLENDER_ID_CLIENT_ID']
|
||||
subclient_id = current_app.config['BLENDER_ID_SUBCLIENT_ID']
|
||||
|
||||
log.debug('Validating subclient token %s for Blender ID user %s', scst, user_id)
|
||||
payload = {'client_id': client_id,
|
||||
'subclient_id': subclient_id,
|
||||
'user_id': user_id,
|
||||
'scst': scst}
|
||||
url = '{0}/subclients/validate_token'.format(authentication.blender_id_endpoint())
|
||||
log.debug('POSTing to %r', url)
|
||||
|
||||
# POST to Blender ID, handling errors as negative verification results.
|
||||
try:
|
||||
r = requests.post(url, data=payload)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
log.error('Connection error trying to POST to %s, handling as invalid token.', url)
|
||||
return None
|
||||
|
||||
if r.status_code != 200:
|
||||
log.info('Token invalid, HTTP status %i returned', r.status_code)
|
||||
return None
|
||||
|
||||
resp = r.json()
|
||||
if resp['status'] != 'success':
|
||||
log.warning('Failed response from %s: %s', url, resp)
|
||||
return None
|
||||
|
||||
return resp['user']
|
||||
|
||||
|
||||
def find_user_in_db(user_id, scst, email, full_name):
|
||||
users = current_app.data.driver.db['users']
|
||||
|
||||
query = {'auth': {'$elemMatch': {'user_id': user_id, 'provider': 'blender-id'}}}
|
||||
log.debug('Querying: %s', query)
|
||||
db_user = users.find_one(query)
|
||||
|
||||
if db_user:
|
||||
log.debug('User %r already in our database, updating with info from Blender ID.', user_id)
|
||||
db_user['full_name'] = full_name
|
||||
db_user['email'] = email
|
||||
|
||||
auth = next(auth for auth in db_user['auth'] if auth['provider'] == 'blender-id')
|
||||
auth['token'] = scst
|
||||
return db_user
|
||||
|
||||
log.debug('User %r not yet in our database, create a new one.', user_id)
|
||||
db_user = authentication.create_new_user_document(email, user_id, full_name, token=scst)
|
||||
db_user['username'] = authentication.make_unique_username(email)
|
||||
return db_user
|
@@ -134,6 +134,15 @@ def create_new_user(email, username, user_id):
|
||||
@returns: the user ID from our local database.
|
||||
"""
|
||||
|
||||
user_data = create_new_user_document(email, user_id, username)
|
||||
r = post_internal('users', user_data)
|
||||
user_id = r[0]['_id']
|
||||
return user_id
|
||||
|
||||
|
||||
def create_new_user_document(email, user_id, username, token=''):
|
||||
"""Creates a new user document, without storing it in MongoDB."""
|
||||
|
||||
user_data = {
|
||||
'full_name': username,
|
||||
'username': username,
|
||||
@@ -141,14 +150,12 @@ def create_new_user(email, username, user_id):
|
||||
'auth': [{
|
||||
'provider': 'blender-id',
|
||||
'user_id': str(user_id),
|
||||
'token': ''}],
|
||||
'token': token}],
|
||||
'settings': {
|
||||
'email_communications': 1
|
||||
}
|
||||
}
|
||||
r = post_internal('users', user_data)
|
||||
user_id = r[0]['_id']
|
||||
return user_id
|
||||
return user_data
|
||||
|
||||
|
||||
def make_unique_username(email):
|
||||
|
@@ -64,3 +64,6 @@ FILE_LINK_VALIDITY = defaultdict(
|
||||
gcs=3600 * 23, # 23 hours for Google Cloud Storage.
|
||||
)
|
||||
|
||||
# Client and Subclient IDs for Blender ID
|
||||
BLENDER_ID_CLIENT_ID = 'SPECIAL-SNOWFLAKE-57'
|
||||
BLENDER_ID_SUBCLIENT_ID = 'PILLAR'
|
||||
|
Reference in New Issue
Block a user