This requires pillar-python-sdk c8eec9fa9d8a198df198538a38ca1ad2367bb3e6 or newer.
129 lines
3.7 KiB
Python
129 lines
3.7 KiB
Python
import logging
|
|
|
|
import flask
|
|
from werkzeug.local import LocalProxy
|
|
|
|
from pillar.api.utils import authorization
|
|
from pillar.extension import PillarExtension
|
|
|
|
EXTENSION_NAME = 'cloud'
|
|
ROLES_TO_BE_SUBSCRIBER = {'demo', 'subscriber', 'admin'} # TODO: get rid of this, use 'subscriber' cap
|
|
|
|
|
|
class CloudExtension(PillarExtension):
|
|
has_context_processor = True
|
|
|
|
def __init__(self):
|
|
self._log = logging.getLogger('%s.CloudExtension' % __name__)
|
|
|
|
@property
|
|
def name(self):
|
|
return EXTENSION_NAME
|
|
|
|
def flask_config(self):
|
|
"""Returns extension-specific defaults for the Flask configuration.
|
|
|
|
Use this to set sensible default values for configuration settings
|
|
introduced by the extension.
|
|
|
|
:rtype: dict
|
|
"""
|
|
|
|
# Just so that it registers the management commands.
|
|
from . import cli
|
|
|
|
return {}
|
|
|
|
def eve_settings(self):
|
|
"""Returns extensions to the Eve settings.
|
|
|
|
Currently only the DOMAIN key is used to insert new resources into
|
|
Eve's configuration.
|
|
|
|
:rtype: dict
|
|
"""
|
|
|
|
return {}
|
|
|
|
def blueprints(self):
|
|
"""Returns the list of top-level blueprints for the extension.
|
|
|
|
These blueprints will be mounted at the url prefix given to
|
|
app.load_extension().
|
|
|
|
:rtype: list of flask.Blueprint objects.
|
|
"""
|
|
from . import routes
|
|
import cloud.stats.routes
|
|
return [
|
|
routes.blueprint,
|
|
cloud.stats.routes.blueprint,
|
|
]
|
|
|
|
@property
|
|
def template_path(self):
|
|
import os.path
|
|
return os.path.join(os.path.dirname(__file__), 'templates')
|
|
|
|
@property
|
|
def static_path(self):
|
|
import os.path
|
|
return os.path.join(os.path.dirname(__file__), 'static')
|
|
|
|
def context_processor(self):
|
|
return {
|
|
'current_user_is_subscriber': authorization.user_has_cap('subscriber')
|
|
}
|
|
|
|
def setup_app(self, app):
|
|
"""Links certain roles to the subscriber role.
|
|
|
|
This means that users who get the subscriber role also get this linked
|
|
role, and when the subscriber role is revoked, the linked role is also
|
|
revoked.
|
|
"""
|
|
|
|
from pillar.api.service import signal_user_changed_role
|
|
from . import routes
|
|
|
|
signal_user_changed_role.connect(self._user_changed_role)
|
|
routes.setup_app(app)
|
|
|
|
def _user_changed_role(self, sender, user: dict):
|
|
from pillar.api import service
|
|
|
|
linked_roles = {'flamenco-user', 'attract-user'}
|
|
link_to = {'subscriber', 'demo'}
|
|
user_roles = set(user.get('roles', []))
|
|
|
|
# Determine what to do
|
|
has_linked_roles = not (linked_roles - user_roles)
|
|
has_link_to = bool(link_to.intersection(user_roles))
|
|
action = ''
|
|
if has_link_to and not has_linked_roles:
|
|
self._log.info('Granting roles %s to user %s', linked_roles, user['_id'])
|
|
action = 'grant'
|
|
elif not has_link_to and has_linked_roles:
|
|
self._log.info('Revoking roles %s from user %s', linked_roles, user['_id'])
|
|
action = 'revoke'
|
|
|
|
if not action:
|
|
return
|
|
|
|
# Avoid infinite loops while we're changing the user's roles.
|
|
service.signal_user_changed_role.disconnect(self._user_changed_role)
|
|
try:
|
|
if linked_roles:
|
|
service.do_badger(action, roles=linked_roles, user_id=user['_id'])
|
|
finally:
|
|
service.signal_user_changed_role.connect(self._user_changed_role)
|
|
|
|
|
|
def _get_current_cloud():
|
|
"""Returns the Cloud extension of the current application."""
|
|
return flask.current_app.pillar_extensions[EXTENSION_NAME]
|
|
|
|
|
|
current_cloud = LocalProxy(_get_current_cloud)
|
|
"""Cloud extension of the current app."""
|