Andrew Bogott has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/333983 )
Change subject: Horizon: Forward some custom files from liberty ...................................................................... Horizon: Forward some custom files from liberty Change-Id: I99b29bc0195f243c13dfd313c23bb0140c820604 --- A modules/openstack/files/mitaka/horizon/keystone_policy.json A modules/openstack/files/mitaka/horizon/openstack_auth/backend.py A modules/openstack/files/mitaka/horizon/puppet_group_add.py A modules/openstack/files/mitaka/horizon/puppet_prefix_tab_enable.py A modules/openstack/files/mitaka/horizon/puppet_tab_enable.py 5 files changed, 465 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/puppet refs/changes/83/333983/1 diff --git a/modules/openstack/files/mitaka/horizon/keystone_policy.json b/modules/openstack/files/mitaka/horizon/keystone_policy.json new file mode 100644 index 0000000..0bd9546 --- /dev/null +++ b/modules/openstack/files/mitaka/horizon/keystone_policy.json @@ -0,0 +1,183 @@ +{ + "admin_required": "role:admin or is_admin:1", + "service_role": "role:service", + "service_or_admin": "rule:admin_required or rule:service_role", + "owner" : "user_id:%(user_id)s", + "admin_or_owner": "rule:admin_required or rule:owner", + "token_subject": "user_id:%(target.token.user_id)s", + "admin_or_token_subject": "rule:admin_required or rule:token_subject", + + "default": "rule:admin_required", + + "identity:get_region": "", + "identity:list_regions": "rule:admin_required", + "identity:create_region": "rule:admin_required", + "identity:update_region": "rule:admin_required", + "identity:delete_region": "rule:admin_required", + + "identity:get_service": "", + "identity:list_services": "", + "identity:create_service": "rule:admin_required", + "identity:update_service": "rule:admin_required", + "identity:delete_service": "rule:admin_required", + + "identity:get_endpoint": "", + "identity:list_endpoints": "", + "identity:create_endpoint": "rule:admin_required", + "identity:update_endpoint": "rule:admin_required", + "identity:delete_endpoint": "rule:admin_required", + + "identity:get_domain": "rule:admin_required", + "identity:list_domains": "rule:admin_required", + "identity:create_domain": "rule:admin_required", + "identity:update_domain": "rule:admin_required", + "identity:delete_domain": "rule:admin_required", + + "identity:get_project": "rule:admin_required", + "identity:list_projects": "role:admin_required", + "identity:list_user_projects": "", + "identity:create_project": "rule:admin_required", + "identity:update_project": "rule:admin_required", + "identity:delete_project": "rule:admin_required", + + "identity:get_user": "rule:admin_required", + "identity:list_users": "rule:admin_required", + "identity:create_user": "rule:admin_required", + "identity:update_user": "rule:admin_required", + "identity:delete_user": "rule:admin_required", + "identity:change_password": "rule:admin_or_owner", + + "identity:get_group": "rule:admin_required", + "identity:list_groups": "rule:admin_required", + "identity:list_groups_for_user": "rule:admin_or_owner", + "identity:create_group": "rule:admin_required", + "identity:update_group": "rule:admin_required", + "identity:delete_group": "rule:admin_required", + "identity:list_users_in_group": "rule:admin_required", + "identity:remove_user_from_group": "rule:admin_required", + "identity:check_user_in_group": "rule:admin_required", + "identity:add_user_to_group": "rule:admin_required", + + "identity:get_credential": "rule:admin_required", + "identity:list_credentials": "rule:admin_required", + "identity:create_credential": "rule:admin_required", + "identity:update_credential": "rule:admin_required", + "identity:delete_credential": "rule:admin_required", + + "identity:ec2_get_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)", + "identity:ec2_list_credentials": "rule:admin_or_owner", + "identity:ec2_create_credential": "rule:admin_or_owner", + "identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)", + + "identity:get_role": "", + "identity:list_roles": "rule:admin_required", + "identity:create_role": "rule:admin_required", + "identity:update_role": "rule:admin_required", + "identity:delete_role": "rule:admin_required", + + "identity:check_grant": "rule:admin_required", + "identity:list_grants": "rule:admin_required", + "identity:create_grant": "rule:admin_required", + "identity:revoke_grant": "rule:admin_required", + + "identity:list_role_assignments": "rule:admin_required", + + "identity:get_policy": "rule:admin_required", + "identity:list_policies": "rule:admin_required", + "identity:create_policy": "rule:admin_required", + "identity:update_policy": "rule:admin_required", + "identity:delete_policy": "rule:admin_required", + + "identity:check_token": "rule:admin_or_token_subject", + "identity:validate_token": "rule:service_admin_or_token_subject", + "identity:validate_token_head": "rule:service_or_admin", + "identity:revocation_list": "rule:service_or_admin", + "identity:revoke_token": "rule:admin_or_token_subject", + + "identity:create_trust": "user_id:%(trust.trustor_user_id)s", + "identity:list_trusts": "rule:admin_required", + "identity:list_roles_for_trust": "", + "identity:get_role_for_trust": "", + "identity:delete_trust": "", + + "identity:create_consumer": "rule:admin_required", + "identity:get_consumer": "rule:admin_required", + "identity:list_consumers": "rule:admin_required", + "identity:delete_consumer": "rule:admin_required", + "identity:update_consumer": "rule:admin_required", + + "identity:authorize_request_token": "rule:admin_required", + "identity:list_access_token_roles": "rule:admin_required", + "identity:get_access_token_role": "rule:admin_required", + "identity:list_access_tokens": "rule:admin_required", + "identity:get_access_token": "rule:admin_required", + "identity:delete_access_token": "rule:admin_required", + + "identity:list_projects_for_endpoint": "rule:admin_required", + "identity:add_endpoint_to_project": "rule:admin_required", + "identity:check_endpoint_in_project": "rule:admin_required", + "identity:list_endpoints_for_project": "rule:admin_required", + "identity:remove_endpoint_from_project": "rule:admin_required", + + "identity:create_endpoint_group": "rule:admin_required", + "identity:list_endpoint_groups": "rule:admin_required", + "identity:get_endpoint_group": "rule:admin_required", + "identity:update_endpoint_group": "rule:admin_required", + "identity:delete_endpoint_group": "rule:admin_required", + "identity:list_projects_associated_with_endpoint_group": "rule:admin_required", + "identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required", + "identity:get_endpoint_group_in_project": "rule:admin_required", + "identity:list_endpoint_groups_for_project": "rule:admin_required", + "identity:add_endpoint_group_to_project": "rule:admin_required", + "identity:remove_endpoint_group_from_project": "rule:admin_required", + + "identity:create_identity_provider": "rule:admin_required", + "identity:list_identity_providers": "rule:admin_required", + "identity:get_identity_providers": "rule:admin_required", + "identity:update_identity_provider": "rule:admin_required", + "identity:delete_identity_provider": "rule:admin_required", + + "identity:create_protocol": "rule:admin_required", + "identity:update_protocol": "rule:admin_required", + "identity:get_protocol": "rule:admin_required", + "identity:list_protocols": "rule:admin_required", + "identity:delete_protocol": "rule:admin_required", + + "identity:create_mapping": "rule:admin_required", + "identity:get_mapping": "rule:admin_required", + "identity:list_mappings": "rule:admin_required", + "identity:delete_mapping": "rule:admin_required", + "identity:update_mapping": "rule:admin_required", + + "identity:create_service_provider": "rule:admin_required", + "identity:list_service_providers": "rule:admin_required", + "identity:get_service_provider": "rule:admin_required", + "identity:update_service_provider": "rule:admin_required", + "identity:delete_service_provider": "rule:admin_required", + + "identity:get_auth_catalog": "", + "identity:get_auth_projects": "", + "identity:get_auth_domains": "", + + "identity:list_projects_for_groups": "", + "identity:list_domains_for_groups": "", + + "identity:list_revoke_events": "", + + "identity:create_policy_association_for_endpoint": "rule:admin_required", + "identity:check_policy_association_for_endpoint": "rule:admin_required", + "identity:delete_policy_association_for_endpoint": "rule:admin_required", + "identity:create_policy_association_for_service": "rule:admin_required", + "identity:check_policy_association_for_service": "rule:admin_required", + "identity:delete_policy_association_for_service": "rule:admin_required", + "identity:create_policy_association_for_region_and_service": "rule:admin_required", + "identity:check_policy_association_for_region_and_service": "rule:admin_required", + "identity:delete_policy_association_for_region_and_service": "rule:admin_required", + "identity:get_policy_for_endpoint": "rule:admin_required", + "identity:list_endpoints_for_policy": "rule:admin_required", + + "identity:create_domain_config": "rule:admin_required", + "identity:get_domain_config": "rule:admin_required", + "identity:update_domain_config": "rule:admin_required", + "identity:delete_domain_config": "rule:admin_required" +} diff --git a/modules/openstack/files/mitaka/horizon/openstack_auth/backend.py b/modules/openstack/files/mitaka/horizon/openstack_auth/backend.py new file mode 100644 index 0000000..10f5da4 --- /dev/null +++ b/modules/openstack/files/mitaka/horizon/openstack_auth/backend.py @@ -0,0 +1,264 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Module defining the Django auth backend class for the Keystone API. """ + +import datetime +import logging +import pytz + +from django.conf import settings +from django.utils.module_loading import import_string # noqa +from django.utils.translation import ugettext_lazy as _ +from keystoneclient import exceptions as keystone_exceptions + +from openstack_auth import exceptions +from openstack_auth import user as auth_user +from openstack_auth import utils + + +LOG = logging.getLogger(__name__) + + +KEYSTONE_CLIENT_ATTR = "_keystoneclient" + + +class KeystoneBackend(object): + """Django authentication backend for use with ``django.contrib.auth``.""" + + def __init__(self): + self._auth_plugins = None + + @property + def auth_plugins(self): + if self._auth_plugins is None: + plugins = getattr( + settings, + 'AUTHENTICATION_PLUGINS', + ['openstack_auth.plugin.password.PasswordPlugin', + 'openstack_auth.plugin.token.TokenPlugin']) + + self._auth_plugins = [import_string(p)() for p in plugins] + + return self._auth_plugins + + def check_auth_expiry(self, auth_ref, margin=None): + if not utils.is_token_valid(auth_ref, margin): + msg = _("The authentication token issued by the Identity service " + "has expired.") + LOG.warning("The authentication token issued by the Identity " + "service appears to have expired before it was " + "issued. This may indicate a problem with either your " + "server or client configuration.") + raise exceptions.KeystoneAuthException(msg) + return True + + def get_user(self, user_id): + """Returns the current user from the session data. + + If authenticated, this return the user object based on the user ID + and session data. + + Note: this required monkey-patching the ``contrib.auth`` middleware + to make the ``request`` object available to the auth backend class. + """ + if (hasattr(self, 'request') and + user_id == self.request.session["user_id"]): + token = self.request.session['token'] + endpoint = self.request.session['region_endpoint'] + services_region = self.request.session['services_region'] + user = auth_user.create_user_from_token(self.request, token, + endpoint, services_region) + return user + else: + return None + + def authenticate(self, auth_url=None, **kwargs): + """Authenticates a user via the Keystone Identity API.""" + LOG.debug('Beginning user authentication') + + if not auth_url: + auth_url = settings.OPENSTACK_KEYSTONE_URL + + auth_url = utils.fix_auth_url_version(auth_url) + + for plugin in self.auth_plugins: + unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs) + + if unscoped_auth: + break + else: + msg = _('No authentication backend could be determined to ' + 'handle the provided credentials.') + LOG.warn('No authentication backend could be determined to ' + 'handle the provided credentials. This is likely a ' + 'configuration error that should be addressed.') + raise exceptions.KeystoneAuthException(msg) + + session = utils.get_session() + keystone_client_class = utils.get_keystone_client().Client + + try: + unscoped_auth_ref = unscoped_auth.get_access(session) + except keystone_exceptions.ConnectionRefused as exc: + LOG.error(str(exc)) + msg = _('Unable to establish connection to keystone endpoint.') + raise exceptions.KeystoneAuthException(msg) + except (keystone_exceptions.Unauthorized, + keystone_exceptions.Forbidden, + keystone_exceptions.NotFound) as exc: + LOG.debug(str(exc)) + raise exceptions.KeystoneAuthException(_('Invalid credentials.')) + except (keystone_exceptions.ClientException, + keystone_exceptions.AuthorizationFailure) as exc: + msg = _("An error occurred authenticating. " + "Please try again later.") + LOG.debug(str(exc)) + raise exceptions.KeystoneAuthException(msg) + + # Check expiry for our unscoped auth ref. + self.check_auth_expiry(unscoped_auth_ref) + + projects = plugin.list_projects(session, + unscoped_auth, + unscoped_auth_ref) + # Attempt to scope only to enabled projects + projects = [project for project in projects if project.enabled] + + # Abort if there are no projects for this user + if not projects: + msg = _('You are not authorized for any projects.') + raise exceptions.KeystoneAuthException(msg) + + # the recent project id a user might have set in a cookie + recent_project = None + request = kwargs.get('request') + + if request: + # Grab recent_project found in the cookie, try to scope + # to the last project used. + recent_project = request.COOKIES.get('recent_project') + + # if a most recent project was found, try using it first + if recent_project: + for pos, project in enumerate(projects): + if project.id == recent_project: + # move recent project to the beginning + projects.pop(pos) + projects.insert(0, project) + break + + for project in projects: + token = unscoped_auth_ref.auth_token + scoped_auth = utils.get_token_auth_plugin(auth_url, + token=token, + project_id=project.id) + + try: + scoped_auth_ref = scoped_auth.get_access(session) + except (keystone_exceptions.ClientException, + keystone_exceptions.AuthorizationFailure): + pass + else: + break + else: + msg = _("Unable to authenticate to any available projects.") + raise exceptions.KeystoneAuthException(msg) + + # Check expiry for our new scoped token. + self.check_auth_expiry(scoped_auth_ref) + + interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public') + + # If we made it here we succeeded. Create our User! + unscoped_token = unscoped_auth_ref.auth_token + user = auth_user.create_user_from_token( + request, + auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token), + scoped_auth_ref.service_catalog.url_for(endpoint_type=interface)) + + if request is not None: + request.session['unscoped_token'] = unscoped_token + request.user = user + if 'extended_session' in kwargs and kwargs['extended_session']: + timeout = getattr(settings, "SESSION_TIMEOUT", 86400) + else: + timeout = getattr(settings, "SESSION_SHORT_TIMEOUT", 1800) + token_life = user.token.expires - datetime.datetime.now(pytz.utc) + + # Fix for https://bugs.launchpad.net/django-openstack-auth/+bug/1562452: + session_time = min(timeout, int(token_life.total_seconds())) + request.session.set_expiry(session_time) + + scoped_client = keystone_client_class(session=session, + auth=scoped_auth) + + # Support client caching to save on auth calls. + setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client) + + LOG.debug('Authentication completed.') + return user + + def get_group_permissions(self, user, obj=None): + """Returns an empty set since Keystone doesn't support "groups".""" + # Keystone V3 added "groups". The Auth token response includes the + # roles from the user's Group assignment. It should be fine just + # returning an empty set here. + return set() + + def get_all_permissions(self, user, obj=None): + """Returns a set of permission strings that the user has. + + This permission available to the user is derived from the user's + Keystone "roles". + + The permissions are returned as ``"openstack.{{ role.name }}"``. + """ + if user.is_anonymous() or obj is not None: + return set() + # TODO(gabrielhurley): Integrate policy-driven RBAC + # when supported by Keystone. + role_perms = set(["openstack.roles.%s" % role['name'].lower() + for role in user.roles]) + + services = [] + for service in user.service_catalog: + try: + service_type = service['type'] + except KeyError: + continue + service_regions = [utils.get_endpoint_region(endpoint) for endpoint + in service.get('endpoints', [])] + if user.services_region in service_regions: + services.append(service_type.lower()) + service_perms = set(["openstack.services.%s" % service + for service in services]) + return role_perms | service_perms + + def has_perm(self, user, perm, obj=None): + """Returns True if the given user has the specified permission.""" + if not user.is_active: + return False + return perm in self.get_all_permissions(user, obj) + + def has_module_perms(self, user, app_label): + """Returns True if user has any permissions in the given app_label. + + Currently this matches for the app_label ``"openstack"``. + """ + if not user.is_active: + return False + for perm in self.get_all_permissions(user): + if perm[:perm.index('.')] == app_label: + return True + return False diff --git a/modules/openstack/files/mitaka/horizon/puppet_group_add.py b/modules/openstack/files/mitaka/horizon/puppet_group_add.py new file mode 100644 index 0000000..67f27b7 --- /dev/null +++ b/modules/openstack/files/mitaka/horizon/puppet_group_add.py @@ -0,0 +1,6 @@ +# The name of the panel group to be added to HORIZON_CONFIG. Required. +PANEL_GROUP = 'puppet' +# The display name of the PANEL_GROUP. Required. +PANEL_GROUP_NAME = 'Puppet' +# The name of the dashboard the PANEL_GROUP associated with. Required. +PANEL_GROUP_DASHBOARD = 'project' diff --git a/modules/openstack/files/mitaka/horizon/puppet_prefix_tab_enable.py b/modules/openstack/files/mitaka/horizon/puppet_prefix_tab_enable.py new file mode 100644 index 0000000..4764131 --- /dev/null +++ b/modules/openstack/files/mitaka/horizon/puppet_prefix_tab_enable.py @@ -0,0 +1,5 @@ +PANEL = 'prefixpuppetpanel' +PANEL_GROUP = 'puppet' +PANEL_DASHBOARD = 'project' +ADD_PANEL = ('wikimediapuppettab.prefixpanel.prefixpanel.PrefixPuppetPanel') +AUTO_DISCOVER_STATIC_FILES = True diff --git a/modules/openstack/files/mitaka/horizon/puppet_tab_enable.py b/modules/openstack/files/mitaka/horizon/puppet_tab_enable.py new file mode 100644 index 0000000..a2d62ed --- /dev/null +++ b/modules/openstack/files/mitaka/horizon/puppet_tab_enable.py @@ -0,0 +1,7 @@ +PANEL = 'projectpuppetpanel' +PANEL_GROUP = 'puppet' +PANEL_DASHBOARD = 'project' +ADD_PANEL = ('wikimediapuppettab.projectpanel.ProjectPuppetPanel') +ADD_INSTALLED_APPS = ['wikimediapuppettab'] +AUTO_DISCOVER_STATIC_FILES = True +ADD_SCSS_FILES = ['dashboard/puppet/puppet.scss'] -- To view, visit https://gerrit.wikimedia.org/r/333983 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I99b29bc0195f243c13dfd313c23bb0140c820604 Gerrit-PatchSet: 1 Gerrit-Project: operations/puppet Gerrit-Branch: production Gerrit-Owner: Andrew Bogott <abog...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits