From aa3dc6b30bbf3128c107b92a766245ff1e011b1b Mon Sep 17 00:00:00 2001 From: George Peristerakis Date: Thu, 12 Jun 2014 10:41:39 -0400 Subject: [PATCH] Added a modal dialog with the user credentials details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modal contains all the credentials used to access the user's openstack and EC2 instance. Change-Id: I750b2922a34dd06b2098eff95d817c852a18be07 Implements: blueprint api-access-page-enhancements Co-Authored-By: Cédric Soulas --- .../access_and_security/api_access/tables.py | 10 +- .../access_and_security/api_access/tests.py | 31 ++++- .../access_and_security/api_access/urls.py | 2 + .../access_and_security/api_access/views.py | 107 ++++++++++-------- .../api_access/_credentials.html | 62 ++++++++++ .../api_access/credentials.html | 11 ++ .../static/dashboard/scss/horizon.scss | 5 + .../test/test_data/keystone_data.py | 3 +- 8 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_credentials.html create mode 100644 openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py index 56d5b0dc84..307a834605 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/tables.py @@ -48,6 +48,14 @@ class DownloadOpenRC(tables.LinkAction): url = "horizon:project:access_and_security:api_access:openrc" +class ViewCredentials(tables.LinkAction): + name = "view_credentials" + verbose_name = _("View Credentials") + classes = ("ajax-modal", ) + icon = "plus" + url = "horizon:project:access_and_security:api_access:view_credentials" + + class EndpointsTable(tables.DataTable): api_name = tables.Column('type', verbose_name=_("Service"), @@ -59,4 +67,4 @@ class EndpointsTable(tables.DataTable): name = "endpoints" verbose_name = _("API Endpoints") multi_select = False - table_actions = (DownloadOpenRC, DownloadEC2,) + table_actions = (DownloadOpenRC, DownloadEC2, ViewCredentials) diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/tests.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/tests.py index 88abb9412c..4cf8745c33 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/tests.py @@ -21,7 +21,10 @@ from openstack_dashboard import api from openstack_dashboard.test import helpers as test -EC2_URL = reverse("horizon:project:access_and_security:api_access:ec2") +API_URL = "horizon:project:access_and_security:api_access" +EC2_URL = reverse(API_URL + ":ec2") +OPENRC_URL = reverse(API_URL + ":openrc") +CREDS_URL = reverse(API_URL + ":view_credentials") class APIAccessTests(test.TestCase): @@ -47,3 +50,29 @@ class APIAccessTests(test.TestCase): res = self.client.get(EC2_URL) self.assertEqual(res.status_code, 200) self.assertEqual(res['content-type'], 'application/zip') + + def test_openrc_credentials(self): + res = self.client.get(OPENRC_URL) + self.assertEqual(res.status_code, 200) + openrc = 'project/access_and_security/api_access/openrc.sh.template' + self.assertTemplateUsed(res, openrc) + name = 'export OS_USERNAME="{}"'.format(self.request.user.username) + id = 'export OS_TENANT_ID={}'.format(self.request.user.tenant_id) + self.assertTrue(name in res.content) + self.assertTrue(id in res.content) + + @test.create_stubs({api.keystone: ("list_ec2_credentials",)}) + def test_credential_api(self): + certs = self.ec2.list() + api.keystone.list_ec2_credentials(IsA(HttpRequest), self.user.id) \ + .AndReturn(certs) + + self.mox.ReplayAll() + + res = self.client.get(CREDS_URL) + self.assertEqual(res.status_code, 200) + credentials = 'project/access_and_security/api_access/credentials.html' + self.assertTemplateUsed(res, credentials) + self.assertEqual(self.user.id, res.context['openrc_creds']['user'].id) + self.assertEqual(certs[0].access, + res.context['ec2_creds']['ec2_access_key']) diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/urls.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/urls.py index 2fd3b75418..8c86ae7757 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/urls.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/urls.py @@ -27,4 +27,6 @@ urlpatterns = patterns( '', url(r'^ec2/$', views.download_ec2_bundle, name='ec2'), url(r'^openrc/$', views.download_rc_file, name='openrc'), + url(r'^view_credentials/$', views.CredentialsView.as_view(), + name='view_credentials') ) diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py index cc8636667d..d0aff4646e 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/views.py @@ -21,8 +21,10 @@ from django import http from django import shortcuts from django.template.loader import render_to_string from django.utils.translation import ugettext_lazy as _ +from django.views import generic from horizon import exceptions +from horizon import forms from horizon import messages from openstack_dashboard import api @@ -31,31 +33,16 @@ from openstack_dashboard import api LOG = logging.getLogger(__name__) -def download_ec2_bundle(request): +def _get_ec2_credentials(request): tenant_id = request.user.tenant_id - tenant_name = request.user.tenant_name + all_keys = api.keystone.list_ec2_credentials(request, + request.user.id) - # Gather or create our EC2 credentials - try: - credentials = api.nova.get_x509_credentials(request) - cacert = api.nova.get_x509_root_certificate(request) - - all_keys = api.keystone.list_ec2_credentials(request, - request.user.id) - keys = None - for key in all_keys: - if key.tenant_id == tenant_id: - keys = key - if keys is None: - keys = api.keystone.create_ec2_credentials(request, - request.user.id, - tenant_id) - except Exception: - exceptions.handle(request, - _('Unable to fetch EC2 credentials.'), - redirect=request.build_absolute_uri()) - - # Get our S3 endpoint if it exists + key = next((x for x in all_keys if x.tenant_id == tenant_id), None) + if not key: + key = api.keystone.create_ec2_credentials(request, + request.user.id, + tenant_id) try: s3_endpoint = api.base.url_for(request, 's3', @@ -63,7 +50,6 @@ def download_ec2_bundle(request): except exceptions.ServiceCatalogException: s3_endpoint = None - # Get our EC2 endpoint (it should exist since we just got creds for it) try: ec2_endpoint = api.base.url_for(request, 'ec2', @@ -71,11 +57,36 @@ def download_ec2_bundle(request): except exceptions.ServiceCatalogException: ec2_endpoint = None - # Build the context - context = {'ec2_access_key': keys.access, - 'ec2_secret_key': keys.secret, - 'ec2_endpoint': ec2_endpoint, - 's3_endpoint': s3_endpoint} + return {'ec2_access_key': key.access, + 'ec2_secret_key': key.secret, + 'ec2_endpoint': ec2_endpoint, + 's3_endpoint': s3_endpoint} + + +def _get_openrc_credentials(request): + keystone_url = api.base.url_for(request, + 'identity', + endpoint_type='publicURL') + credentials = dict(tenant_id=request.user.tenant_id, + tenant_name=request.user.tenant_name, + auth_url=keystone_url, + user=request.user, + region=getattr(request.user, 'services_region') or "") + return credentials + + +def download_ec2_bundle(request): + tenant_name = request.user.tenant_name + + # Gather or create our EC2 credentials + try: + credentials = api.nova.get_x509_credentials(request) + cacert = api.nova.get_x509_root_certificate(request) + context = _get_ec2_credentials(request) + except Exception: + exceptions.handle(request, + _('Unable to fetch EC2 credentials.'), + redirect=request.build_absolute_uri()) # Create our file bundle template = 'project/access_and_security/api_access/ec2rc.sh.template' @@ -102,24 +113,9 @@ def download_ec2_bundle(request): def download_rc_file(request): - tenant_id = request.user.tenant_id - tenant_name = request.user.tenant_name - region = request.user.services_region - if region is None: - region = "" - template = 'project/access_and_security/api_access/openrc.sh.template' - try: - keystone_url = api.base.url_for(request, - 'identity', - endpoint_type='publicURL') - - context = {'user': request.user, - 'auth_url': keystone_url, - 'tenant_id': tenant_id, - 'tenant_name': tenant_name, - 'region': region} + context = _get_openrc_credentials(request) response = shortcuts.render(request, template, @@ -127,7 +123,7 @@ def download_rc_file(request): content_type="text/plain") response['Content-Disposition'] = ('attachment; ' 'filename="%s-openrc.sh"' - % tenant_name) + % context['tenant_name']) response['Content-Length'] = str(len(response.content)) return response @@ -135,3 +131,22 @@ def download_rc_file(request): LOG.exception("Exception in DownloadOpenRCForm.") messages.error(request, _('Error Downloading RC File: %s') % e) return shortcuts.redirect(request.build_absolute_uri()) + + +class CredentialsView(forms.ModalFormMixin, generic.TemplateView): + template_name = 'project/access_and_security/api_access/credentials.html' + + def get_context_data(self, **kwargs): + context = super(CredentialsView, self).get_context_data(**kwargs) + try: + context['openrc_creds'] = _get_openrc_credentials(self.request) + except Exception: + exceptions.handle(self.request, + _('Unable to get openrc credentials')) + if api.base.is_service_enabled(self.request, 'ec2'): + try: + context['ec2_creds'] = _get_ec2_credentials(self.request) + except Exception: + exceptions.handle(self.request, + _('Unable to get EC2 credentials')) + return context diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_credentials.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_credentials.html new file mode 100644 index 0000000000..34382255c4 --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_credentials.html @@ -0,0 +1,62 @@ +{% extends "horizon/common/_modal.html" %} +{% load i18n %} +{% load url from future %} + +{% block modal-header %}{% trans "User Credentials" %}{% endblock %} + +{% block modal-body %} +
+
+ {% if openrc_creds %} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ {% endif %} + {% if ec2_creds %} +
+
+
+ +
+
+ +
+ {% if ec2_creds.ec2_access_key %} +
+ + +
+ {% endif %} + {% if ec2_creds.ec2_secret_key %} +
+ + +
+ {% endif %} +
+
+ {% endif %} +
+
+{% endblock %} + +{% block modal-footer %} + {% trans "Close" %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html new file mode 100644 index 0000000000..b0fd58e374 --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/credentials.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "User Credentials" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("User Credentials Details") %} +{% endblock page_header %} + +{% block main %} + {% include 'project/access_and_security/api_access/_credentials.html' %} +{% endblock %} diff --git a/openstack_dashboard/static/dashboard/scss/horizon.scss b/openstack_dashboard/static/dashboard/scss/horizon.scss index fe31af23fd..bb4ab85dee 100644 --- a/openstack_dashboard/static/dashboard/scss/horizon.scss +++ b/openstack_dashboard/static/dashboard/scss/horizon.scss @@ -2196,6 +2196,11 @@ label.log-length { } } +/* Read only text fields */ +.form-control[readonly], .view-credentials input { + cursor: text; +} + /**** Popover ****/ a.link-popover { cursor: default; } diff --git a/openstack_dashboard/test/test_data/keystone_data.py b/openstack_dashboard/test/test_data/keystone_data.py index dcc31314db..3d8cde0889 100644 --- a/openstack_dashboard/test/test_data/keystone_data.py +++ b/openstack_dashboard/test/test_data/keystone_data.py @@ -364,5 +364,6 @@ def data(TEST): TEST.tokens.unscoped_token = unscoped_token access_secret = ec2.EC2(ec2.CredentialsManager, {"access": "access", - "secret": "secret"}) + "secret": "secret", + "tenant_id": tenant.id}) TEST.ec2.add(access_secret)