From a9e9d715bbeb4d65c229e414f56800ca0ad6f370 Mon Sep 17 00:00:00 2001 From: kenji-ishii Date: Wed, 2 Dec 2015 22:50:53 +0900 Subject: [PATCH] Add function to recreate ec2 credential At the moment, we can view the credentials of ec2 in Horizon. But we can not delete or update it. When information of access key and secret key were leaked out, it would be nice if we can regenerate a credentials via not only CLI but also Horizon. Thus we need to provide a function user can change ec2 credentials when user want to do it. Change-Id: Ibaebad73eb9a869564bd7d52e2f6b69a503a60c3 Closes-Bug: #1519764 --- openstack_dashboard/api/keystone.py | 4 ++ .../access_and_security/api_access/forms.py | 68 +++++++++++++++++++ .../access_and_security/api_access/tables.py | 28 +++++++- .../access_and_security/api_access/tests.py | 39 +++++++++++ .../access_and_security/api_access/urls.py | 4 +- .../access_and_security/api_access/views.py | 16 ++++- .../api_access/_recreate_credentials.html | 6 ++ .../api_access/recreate_credentials.html | 7 ++ .../material/static/horizon/_icons.scss | 1 + 9 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 openstack_dashboard/dashboards/project/access_and_security/api_access/forms.py create mode 100644 openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_recreate_credentials.html create mode 100644 openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/recreate_credentials.html diff --git a/openstack_dashboard/api/keystone.py b/openstack_dashboard/api/keystone.py index 5a3e61a753..b4b8a13485 100644 --- a/openstack_dashboard/api/keystone.py +++ b/openstack_dashboard/api/keystone.py @@ -715,6 +715,10 @@ def get_user_ec2_credentials(request, user_id, access_token): return ec2_manager(request).get(user_id, access_token) +def delete_user_ec2_credentials(request, user_id, access_token): + return ec2_manager(request).delete(user_id, access_token) + + def keystone_can_edit_domain(): backend_settings = getattr(settings, "OPENSTACK_KEYSTONE_BACKEND", {}) can_edit_domain = backend_settings.get('can_edit_domain', True) diff --git a/openstack_dashboard/dashboards/project/access_and_security/api_access/forms.py b/openstack_dashboard/dashboards/project/access_and_security/api_access/forms.py new file mode 100644 index 0000000000..39f2921fc5 --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/api_access/forms.py @@ -0,0 +1,68 @@ +# Copyright 2016 NEC Corporation +# +# 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. + +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from horizon import messages + +from openstack_dashboard import api +from openstack_dashboard import policy + + +def get_ec2_credentials(request): + if not policy.check((("identity", "identity:ec2_list_credentials"),), + request): + return None + + project_id = request.user.project_id + all_keys = api.keystone.list_ec2_credentials(request, + request.user.id) + keys = [x for x in all_keys if x.tenant_id == project_id] + if not keys: + return None + return {'ec2_access_key': keys[0].access, + 'ec2_secret_key': keys[0].secret} + + +class RecreateCredentials(forms.SelfHandlingForm): + + def handle(self, request, context): + try: + credential = get_ec2_credentials(request) + if credential: + api.keystone.delete_user_ec2_credentials( + request, + request.user.id, + credential['ec2_access_key']) + except Exception: + exceptions.handle( + request, _('Unable to recreate ec2 credentials. ' + 'Failed to delete ec2 credentials.')) + return False + + try: + api.keystone.create_ec2_credentials( + request, + request.user.id, + request.user.project_id) + message = _('Successfully recreated ec2 credentials.') + messages.success(request, message) + return True + except Exception: + exceptions.handle( + request, _('Unable to recreate ec2 credentials. ' + 'Failed to create ec2 credentials.')) + return False 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 1f547a3bdb..03ef908068 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 @@ -19,6 +19,9 @@ from openstack_auth import utils from horizon import tables from openstack_dashboard import api +from openstack_dashboard.dashboards.project.access_and_security.api_access \ + import forms as project_forms +from openstack_dashboard import policy def pretty_service_names(name): @@ -69,6 +72,29 @@ class ViewCredentials(tables.LinkAction): url = "horizon:project:access_and_security:api_access:view_credentials" +class RecreateCredentials(tables.LinkAction): + name = "recreate_credentials" + verbose_name = _("Recreate EC2 Credentials") + classes = ("ajax-modal", "btn-danger") + icon = "refresh" + url = \ + "horizon:project:access_and_security:api_access:recreate_credentials" + policy_rules = (("compute", "compute_extension:certificates")) + + def allowed(self, request, datum=None): + try: + target = {"target.credential.user_id": request.user.id} + if (api.base.is_service_enabled(request, 'ec2') and + project_forms.get_ec2_credentials(request) and + policy.check((("identity", "identity:ec2_create_credential"), + ("identity", "identity:ec2_delete_credential")), + request, target=target)): + return True + except Exception: + pass + return False + + class EndpointsTable(tables.DataTable): api_name = tables.Column('type', verbose_name=_("Service"), @@ -81,4 +107,4 @@ class EndpointsTable(tables.DataTable): verbose_name = _("API Endpoints") multi_select = False table_actions = (DownloadOpenRCv2, DownloadOpenRC, DownloadEC2, - ViewCredentials) + ViewCredentials, RecreateCredentials) 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 0c1d18c4a8..bbad063738 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 @@ -22,11 +22,13 @@ from openstack_dashboard import api from openstack_dashboard.test import helpers as test +INDEX_URL = reverse('horizon:project:access_and_security:index') API_URL = "horizon:project:access_and_security:api_access" EC2_URL = reverse(API_URL + ":ec2") OPENRC_URL = reverse(API_URL + ":openrc") OPENRCV2_URL = reverse(API_URL + ":openrcv2") CREDS_URL = reverse(API_URL + ":view_credentials") +RECREATE_CREDS_URL = reverse(API_URL + ":recreate_credentials") class APIAccessTests(test.TestCase): @@ -96,3 +98,40 @@ class APIAccessTests(test.TestCase): self.assertEqual(self.user.id, res.context['openrc_creds']['user'].id) self.assertEqual(certs[0].access, res.context['ec2_creds']['ec2_access_key']) + + @test.create_stubs({api.keystone: ("list_ec2_credentials", + "create_ec2_credentials", + "delete_user_ec2_credentials",)}) + def _test_recreate_user_credentials(self, exists_credentials=True): + old_creds = self.ec2.list() if exists_credentials else [] + new_creds = self.ec2.first() + api.keystone.list_ec2_credentials( + IsA(HttpRequest), + self.user.id).AndReturn(old_creds) + if exists_credentials: + api.keystone.delete_user_ec2_credentials( + IsA(HttpRequest), + self.user.id, + old_creds[0].access).AndReturn([]) + api.keystone.create_ec2_credentials( + IsA(HttpRequest), + self.user.id, + self.tenant.id).AndReturn(new_creds) + + self.mox.ReplayAll() + + res_get = self.client.get(RECREATE_CREDS_URL) + self.assertEqual(res_get.status_code, 200) + credentials = \ + 'project/access_and_security/api_access/recreate_credentials.html' + self.assertTemplateUsed(res_get, credentials) + + res_post = self.client.post(RECREATE_CREDS_URL) + self.assertNoFormErrors(res_post) + self.assertRedirectsNoFollow(res_post, INDEX_URL) + + def test_recreate_user_credentials(self): + self._test_recreate_user_credentials() + + def test_recreate_user_credentials_with_no_existing_creds(self): + self._test_recreate_user_credentials(exists_credentials=False) 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 51b090f229..0405bfae5f 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 @@ -29,5 +29,7 @@ urlpatterns = patterns( url(r'^openrc/$', views.download_rc_file, name='openrc'), url(r'^openrcv2/$', views.download_rc_file_v2, name='openrcv2'), url(r'^view_credentials/$', views.CredentialsView.as_view(), - name='view_credentials') + name='view_credentials'), + url(r'^recreate_ec2_credentials/$', + views.RecreateCredentialsView.as_view(), name='recreate_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 b5bea20a94..3cd0f32296 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 @@ -17,6 +17,7 @@ import logging import tempfile import zipfile +from django.core.urlresolvers import reverse_lazy from django import http from django import shortcuts from django.template.loader import render_to_string @@ -30,7 +31,8 @@ from horizon import messages from horizon import views from openstack_dashboard import api - +from openstack_dashboard.dashboards.project.access_and_security.api_access \ + import forms as project_forms LOG = logging.getLogger(__name__) @@ -167,3 +169,15 @@ class CredentialsView(forms.ModalFormMixin, views.HorizonTemplateView): exceptions.handle(self.request, _('Unable to get EC2 credentials')) return context + + +class RecreateCredentialsView(forms.ModalFormView): + form_class = project_forms.RecreateCredentials + form_id = "recreate_credentials" + modal_header = _("Recreate EC2 Credentials") + template_name = \ + 'project/access_and_security/api_access/recreate_credentials.html' + submit_label = _("Recreate EC2 Credentials") + submit_url = reverse_lazy( + "horizon:project:access_and_security:api_access:recreate_credentials") + success_url = reverse_lazy('horizon:project:access_and_security:index') diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_recreate_credentials.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_recreate_credentials.html new file mode 100644 index 0000000000..2d7a838ebf --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/_recreate_credentials.html @@ -0,0 +1,6 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block modal-body %} + {% trans "This action cannot be undone. Current EC2 credentials will be deleted and not recoverable." %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/recreate_credentials.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/recreate_credentials.html new file mode 100644 index 0000000000..48c41a608c --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/api_access/recreate_credentials.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Recreate EC2 Credentials" %}{% endblock %} + +{% block main %} + {% include 'project/access_and_security/api_access/_recreate_credentials.html' %} +{% endblock %} diff --git a/openstack_dashboard/themes/material/static/horizon/_icons.scss b/openstack_dashboard/themes/material/static/horizon/_icons.scss index a8c7080b4f..cf5e3adc59 100644 --- a/openstack_dashboard/themes/material/static/horizon/_icons.scss +++ b/openstack_dashboard/themes/material/static/horizon/_icons.scss @@ -46,6 +46,7 @@ $icon-swap: ( plus: 'plus', question-circle: 'help-circle', random: 'shuffle', + refresh: 'refresh', remove: 'close', save: 'floppy', search: 'magnify',