Added "Update from Blender ID" button
Added this button in the /u/ user/embed view, so that admins can easily force a re-check from Blender ID without requiring the user themselves to perform any actions.
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
import collections
|
|
||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from flask import Blueprint
|
from flask import Blueprint, Response
|
||||||
|
import requests
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
|
||||||
from pillar.auth import UserClass
|
from pillar import auth, current_app
|
||||||
from pillar.api.utils import authorization
|
from pillar.api import blender_id
|
||||||
|
from pillar.api.utils import authorization, jsonify
|
||||||
|
from pillar.auth import current_user
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
blueprint = Blueprint('blender_cloud.subscription', __name__)
|
blueprint = Blueprint('blender_cloud.subscription', __name__)
|
||||||
@@ -27,9 +30,6 @@ def update_subscription() -> typing.Tuple[str, int]:
|
|||||||
Returns an empty HTTP response.
|
Returns an empty HTTP response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pillar import auth
|
|
||||||
from pillar.api import blender_id
|
|
||||||
|
|
||||||
my_log: logging.Logger = log.getChild('update_subscription')
|
my_log: logging.Logger = log.getChild('update_subscription')
|
||||||
current_user = auth.get_current_user()
|
current_user = auth.get_current_user()
|
||||||
|
|
||||||
@@ -48,7 +48,58 @@ def update_subscription() -> typing.Tuple[str, int]:
|
|||||||
return '', 204
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
def do_update_subscription(local_user: UserClass, bid_user: dict):
|
@blueprint.route('/update-subscription-for/<user_id>', methods=['POST'])
|
||||||
|
@authorization.require_login(require_cap='admin')
|
||||||
|
def update_subscription_for(user_id: str):
|
||||||
|
"""Updates the user based on their info at Blender ID."""
|
||||||
|
|
||||||
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
|
from pillar.api.utils import str2id
|
||||||
|
|
||||||
|
my_log = log.getChild('update_subscription_for')
|
||||||
|
|
||||||
|
bid_session = requests.Session()
|
||||||
|
bid_session.mount('https://', HTTPAdapter(max_retries=5))
|
||||||
|
bid_session.mount('http://', HTTPAdapter(max_retries=5))
|
||||||
|
|
||||||
|
users_coll = current_app.db('users')
|
||||||
|
db_user = users_coll.find_one({'_id': str2id(user_id)})
|
||||||
|
if not db_user:
|
||||||
|
my_log.warning('User %s not found in database', user_id)
|
||||||
|
return Response(f'User {user_id} not found in our database', status=404)
|
||||||
|
|
||||||
|
log.info('Updating user %s from Blender ID on behalf of %s',
|
||||||
|
db_user['email'], current_user.email)
|
||||||
|
|
||||||
|
bid_user_id = blender_id.get_user_blenderid(db_user)
|
||||||
|
if not bid_user_id:
|
||||||
|
my_log.info('User %s has no Blender ID', user_id)
|
||||||
|
return Response('User has no Blender ID', status=404)
|
||||||
|
|
||||||
|
# Get the user info from Blender ID, and handle errors.
|
||||||
|
api_url = current_app.config['BLENDER_ID_USER_INFO_API']
|
||||||
|
api_token = current_app.config['BLENDER_ID_USER_INFO_TOKEN']
|
||||||
|
url = urljoin(api_url, bid_user_id)
|
||||||
|
resp = bid_session.get(url, headers={'Authorization': f'Bearer {api_token}'})
|
||||||
|
if resp.status_code == 404:
|
||||||
|
my_log.info('User %s has a Blender ID %s but Blender ID itself does not find it',
|
||||||
|
user_id, bid_user_id)
|
||||||
|
return Response(f'User {bid_user_id} does not exist at Blender ID', status=404)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
my_log.info('Error code %s getting user %s from Blender ID (resp = %s)',
|
||||||
|
resp.status_code, user_id, resp.text)
|
||||||
|
return Response(f'Error code {resp.status_code} from Blender ID', status=resp.status_code)
|
||||||
|
|
||||||
|
# Update the user in our database.
|
||||||
|
local_user = auth.UserClass.construct('', db_user)
|
||||||
|
bid_user = resp.json()
|
||||||
|
do_update_subscription(local_user, bid_user)
|
||||||
|
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
|
||||||
|
def do_update_subscription(local_user: auth.UserClass, bid_user: dict):
|
||||||
"""Updates the subscription status of the user given the Blender ID user info.
|
"""Updates the subscription status of the user given the Blender ID user info.
|
||||||
|
|
||||||
Uses the badger service to update the user's roles from Blender ID.
|
Uses the badger service to update the user's roles from Blender ID.
|
||||||
@@ -106,6 +157,10 @@ def do_update_subscription(local_user: UserClass, bid_user: dict):
|
|||||||
user_id, email, ', '.join(sorted(revoke_roles)))
|
user_id, email, ', '.join(sorted(revoke_roles)))
|
||||||
service.do_badger('revoke', roles=revoke_roles, user_id=user_id)
|
service.do_badger('revoke', roles=revoke_roles, user_id=user_id)
|
||||||
|
|
||||||
|
# Re-index the user in the search database.
|
||||||
|
from pillar.api.users import hooks
|
||||||
|
hooks.push_updated_user_to_algolia({'_id': user_id}, {})
|
||||||
|
|
||||||
|
|
||||||
def setup_app(app, url_prefix):
|
def setup_app(app, url_prefix):
|
||||||
log.info('Registering blueprint at %s', url_prefix)
|
log.info('Registering blueprint at %s', url_prefix)
|
||||||
|
@@ -168,6 +168,24 @@ def _compute_token_expiry(token_expires_string):
|
|||||||
return min(blid_expiry, our_expiry)
|
return min(blid_expiry, our_expiry)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_blenderid(db_user: dict) -> str:
|
||||||
|
"""Returns the Blender ID user ID for this Pillar user.
|
||||||
|
|
||||||
|
Takes the string from 'auth.*.user_id' for the '*' where 'provider'
|
||||||
|
is 'blender-id'.
|
||||||
|
|
||||||
|
:returns the user ID, or the empty string when the user has none.
|
||||||
|
"""
|
||||||
|
|
||||||
|
bid_user_ids = [auth['user_id']
|
||||||
|
for auth in db_user['auth']
|
||||||
|
if auth['provider'] == 'blender-id']
|
||||||
|
try:
|
||||||
|
return bid_user_ids[0]
|
||||||
|
except IndexError:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def fetch_blenderid_user() -> dict:
|
def fetch_blenderid_user() -> dict:
|
||||||
"""Returns the user info of the currently logged in user from BlenderID.
|
"""Returns the user info of the currently logged in user from BlenderID.
|
||||||
|
|
||||||
|
@@ -99,8 +99,9 @@ FULL_FILE_ACCESS_ROLES = {'admin', 'subscriber', 'demo'}
|
|||||||
BLENDER_ID_CLIENT_ID = 'SPECIAL-SNOWFLAKE-57'
|
BLENDER_ID_CLIENT_ID = 'SPECIAL-SNOWFLAKE-57'
|
||||||
BLENDER_ID_SUBCLIENT_ID = 'PILLAR'
|
BLENDER_ID_SUBCLIENT_ID = 'PILLAR'
|
||||||
|
|
||||||
# Blender ID user info API endpoint URL and auth token, only used for
|
# Blender ID user info API endpoint URL and auth token, used for
|
||||||
# reconciling subscribers. The token requires the 'userinfo' scope.
|
# reconciling subscribers and updating their info from /u/.
|
||||||
|
# The token requires the 'userinfo' scope.
|
||||||
BLENDER_ID_USER_INFO_API = 'http://blender-id:8000/api/user/'
|
BLENDER_ID_USER_INFO_API = 'http://blender-id:8000/api/user/'
|
||||||
BLENDER_ID_USER_INFO_TOKEN = '-set-in-config-local-'
|
BLENDER_ID_USER_INFO_TOKEN = '-set-in-config-local-'
|
||||||
|
|
||||||
|
@@ -72,6 +72,7 @@
|
|||||||
| none
|
| none
|
||||||
| {% endif %}
|
| {% endif %}
|
||||||
|
|
||||||
|
a.btn.btn-default(href="javascript:update_from_bid()") Update from Blender ID
|
||||||
a.btn.btn-default(href="javascript:$('#user-edit-container').html('')") Cancel
|
a.btn.btn-default(href="javascript:$('#user-edit-container').html('')") Cancel
|
||||||
|
|
||||||
input#submit_edit_user.btn.btn-default(
|
input#submit_edit_user.btn.btn-default(
|
||||||
@@ -103,4 +104,16 @@ script(type="text/javascript").
|
|||||||
|
|
||||||
new Clipboard('.copy-to-clipboard');
|
new Clipboard('.copy-to-clipboard');
|
||||||
|
|
||||||
|
function update_from_bid() {
|
||||||
|
var url = '{{ url_for("blender_cloud.subscription.update_subscription_for", user_id=user.user_id) }}';
|
||||||
|
$.post(url)
|
||||||
|
.done(function(data) {
|
||||||
|
toastr.info('User updated from Blender ID');
|
||||||
|
displayUser('{{ user.user_id }}');
|
||||||
|
})
|
||||||
|
.fail(function(data) {
|
||||||
|
toastr.error(data.responseText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
| {% endblock %}
|
| {% endblock %}
|
||||||
|
Reference in New Issue
Block a user