def update_subscription(): """Updates the subscription status of the current user. Returns an empty HTTP response. """ import pprint from pillar.api import blender_id, service from pillar.api.utils import authentication my_log: logging.Logger = log.getChild('update_subscription') user_id = authentication.current_user_id() bid_user = blender_id.fetch_blenderid_user() if not bid_user: my_log.warning( 'Logged in user %s has no BlenderID account! ' 'Unable to update subscription status.', user_id) return '', 204 # Use the Blender ID email address to check with the store. At least that reduces the # number of email addresses that could be out of sync to two (rather than three when we # use the email address from our local database). try: email = bid_user['email'] except KeyError: my_log.error( 'Blender ID response did not include an email address, ' 'unable to update subscription status: %s', pprint.pformat(bid_user, compact=True)) return 'Internal error', 500 store_user = fetch_subscription_info(email) or {} # Handle the role changes via the badger service functionality. grant_subscriber = store_user.get('cloud_access', 0) == 1 grant_demo = bid_user.get('roles', {}).get('cloud_demo', False) is_subscriber = authorization.user_has_role('subscriber') is_demo = authorization.user_has_role('demo') if grant_subscriber != is_subscriber: action = 'grant' if grant_subscriber else 'revoke' my_log.info('%sing subscriber role to user %s (Blender ID email %s)', action, user_id, email) service.do_badger(action, role='subscriber', user_id=user_id) else: my_log.debug('Not changing subscriber role, grant=%r and is=%s', grant_subscriber, is_subscriber) if grant_demo != is_demo: action = 'grant' if grant_demo else 'revoke' my_log.info('%sing demo role to user %s (Blender ID email %s)', action, user_id, email) service.do_badger(action, role='demo', user_id=user_id) else: my_log.debug('Not changing demo role, grant=%r and is=%s', grant_demo, is_demo) return '', 204
def revoke_admin(user_email): """Revokes the user's flamenco-admin role.""" from pillar.api.service import do_badger _, status = do_badger('revoke', role='flamenco-admin', user_email=user_email) if status != 204: log.error('Unable to find user %s', user_email) return 1 log.info('Done.') return 0
def badger(action, user_email, role): from pillar.api import service with current_app.app_context(): service.fetch_role_to_group_id_map() response, status = service.do_badger(action, role=role, user_email=user_email) if status == 204: log.info('Done.') else: log.info('Response: %s', response) log.info('Status : %i', status)
def refresh_roles(self, user_id: bson.ObjectId) -> typing.Set[str]: """Refreshes the user's roles to own roles + organizations' roles. :returns: the applied set of roles. """ assert isinstance(user_id, bson.ObjectId) from pillar.api.service import do_badger self._log.info('Refreshing roles for user %s', user_id) org_coll = current_app.db('organizations') tokens_coll = current_app.db('tokens') def aggr_roles(coll, match: dict) -> typing.Set[str]: query = coll.aggregate([{ '$match': match }, { '$project': { 'org_roles': 1 } }, { '$unwind': { 'path': '$org_roles' } }, { '$group': { '_id': None, 'org_roles': { '$addToSet': '$org_roles' }, } }]) # If the user has no organizations/tokens at all, the query will have no results. try: org_roles_doc = query.next() except StopIteration: return set() return set(org_roles_doc['org_roles']) # Join all organization-given roles and roles from the tokens collection. org_roles = aggr_roles(org_coll, {'members': user_id}) self._log.debug('Organization-given roles for user %s: %s', user_id, org_roles) token_roles = aggr_roles(tokens_coll, { 'user': user_id, 'expire_time': { "$gt": utcnow() }, }) self._log.debug('Token-given roles for user %s: %s', user_id, token_roles) org_roles.update(token_roles) users_coll = current_app.db('users') user_doc = users_coll.find_one(user_id, projection={'roles': 1}) if not user_doc: self._log.warning( 'Trying refresh roles of non-existing user %s, ignoring', user_id) return set() all_user_roles = set(user_doc.get('roles') or []) existing_org_roles = { role for role in all_user_roles if role.startswith('org-') } grant_roles = org_roles - all_user_roles revoke_roles = existing_org_roles - org_roles if grant_roles: do_badger('grant', roles=grant_roles, user_id=user_id) if revoke_roles: do_badger('revoke', roles=revoke_roles, user_id=user_id) return all_user_roles.union(grant_roles) - revoke_roles
def refresh_roles(self, user_id: bson.ObjectId): """Refreshes the user's roles to own roles + organizations' roles.""" assert isinstance(user_id, bson.ObjectId) from pillar.api.service import do_badger self._log.info('Refreshing roles for user %s', user_id) org_coll = current_app.db('organizations') # Aggregate all org-given roles for this user. query = org_coll.aggregate([{ '$match': { 'members': user_id } }, { '$project': { 'org_roles': 1 } }, { '$unwind': { 'path': '$org_roles' } }, { '$group': { '_id': None, 'org_roles': { '$addToSet': '$org_roles' }, } }]) # If the user has no organizations at all, the query will have no results. try: org_roles_doc = query.next() except StopIteration: org_roles = set() else: org_roles = set(org_roles_doc['org_roles']) users_coll = current_app.db('users') user_doc = users_coll.find_one(user_id, projection={'roles': 1}) if not user_doc: self._log.warning( 'Trying refresh roles of non-existing user %s, ignoring', user_id) return all_user_roles = set(user_doc.get('roles') or []) existing_org_roles = { role for role in all_user_roles if role.startswith('org-') } grant_roles = org_roles - all_user_roles revoke_roles = existing_org_roles - org_roles if grant_roles: do_badger('grant', roles=grant_roles, user_id=user_id) if revoke_roles: do_badger('revoke', roles=revoke_roles, user_id=user_id)