Added a modal dialog with the user credentials details
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 <cedric.soulas@cloudwatt.com>
This commit is contained in:
parent
c88e944b97
commit
aa3dc6b30b
|
@ -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)
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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')
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
||||
<div class="row-fluid view-credentials">
|
||||
<form>
|
||||
{% if openrc_creds %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "User Name" %}</label>
|
||||
<input type="text" class="form-control" readonly value="{{ openrc_creds.user }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "Project Name" %}</label>
|
||||
<input type="text" class="form-control" readonly value="{{ openrc_creds.tenant_name }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "Project ID" %}</label>
|
||||
<input type="text" class="form-control" readonly value="{{ openrc_creds.tenant_id }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "Authentication URL" %}</label>
|
||||
<input type="text" class="form-control" readonly value="{{ openrc_creds.auth_url }}">
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if ec2_creds %}
|
||||
<div class="right">
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "EC2 URL" %}</label><input type="text" class="form-control" readonly value="{{ ec2_creds.ec2_endpoint }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "S3 URL" %}</label><input type="text" class="form-control" readonly value="{{ ec2_creds.s3_endpoint }}">
|
||||
</div>
|
||||
{% if ec2_creds.ec2_access_key %}
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "EC2 Access Key" %}</label>
|
||||
<input type="text" class="form-control" readonly value="{{ ec2_creds.ec2_access_key }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if ec2_creds.ec2_secret_key %}
|
||||
<div class="form-group">
|
||||
<label class="control-label">{% trans "EC2 Secret Key" %}</label>
|
||||
<input type="password" class="form-control" readonly value="{{ ec2_creds.ec2_secret_key }}">
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a href="{% url 'horizon:project:access_and_security:index' %}" class="btn btn-default secondary cancel close">{% trans "Close" %}</a>
|
||||
{% endblock %}
|
|
@ -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 %}
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue