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)
|
# The encoding module (receive notification and report progress)
|
||||||
from modules.encoding import encoding
|
from modules.encoding import encoding
|
||||||
|
from modules.blender_id import blender_id
|
||||||
|
|
||||||
app.register_blueprint(encoding, url_prefix='/encoding')
|
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.
|
@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 = {
|
user_data = {
|
||||||
'full_name': username,
|
'full_name': username,
|
||||||
'username': username,
|
'username': username,
|
||||||
@@ -141,14 +150,12 @@ def create_new_user(email, username, user_id):
|
|||||||
'auth': [{
|
'auth': [{
|
||||||
'provider': 'blender-id',
|
'provider': 'blender-id',
|
||||||
'user_id': str(user_id),
|
'user_id': str(user_id),
|
||||||
'token': ''}],
|
'token': token}],
|
||||||
'settings': {
|
'settings': {
|
||||||
'email_communications': 1
|
'email_communications': 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r = post_internal('users', user_data)
|
return user_data
|
||||||
user_id = r[0]['_id']
|
|
||||||
return user_id
|
|
||||||
|
|
||||||
|
|
||||||
def make_unique_username(email):
|
def make_unique_username(email):
|
||||||
|
@@ -64,3 +64,6 @@ FILE_LINK_VALIDITY = defaultdict(
|
|||||||
gcs=3600 * 23, # 23 hours for Google Cloud Storage.
|
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