BryanDavis has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/328117 )
Change subject: Allow deleting SSH keys ...................................................................... Allow deleting SSH keys Add support for deleting existing SSH keys from the user's LDAP account. Bug: T144711 Change-Id: I1a307bf35fc6f68ef316c23ff037184d696aca42 --- A striker/decorators.py A striker/profile/forms.py M striker/profile/urls.py M striker/profile/utils.py M striker/profile/views.py M striker/templates/profile/settings/ssh-keys.html A striker/templates/profile/settings/ssh-keys/delete-confirm.html 7 files changed, 176 insertions(+), 1 deletion(-) git pull ssh://gerrit.wikimedia.org:29418/labs/striker refs/changes/17/328117/1 diff --git a/striker/decorators.py b/striker/decorators.py new file mode 100644 index 0000000..c5b28b8 --- /dev/null +++ b/striker/decorators.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016 Wikimedia Foundation and contributors. +# All Rights Reserved. +# +# This file is part of Striker. +# +# Striker is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Striker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Striker. If not, see <http://www.gnu.org/licenses/>. + +import functools + +from django import shortcuts +from django import template + + +def confirm_required(template_name, context_builder=None, key='__confirm__'): + """Decorate a view that requires confirmation.""" + def decorator(f): + @functools.wraps(f) + def decorated(request, *args, **kwargs): + if key in request.POST: + return f(request, *args, **kwargs) + if context_builder is not None: + ctx = context_builder(request, *args, **kwargs) + else: + ctx = template.RequestContext(request) + return shortcuts.render_to_response(template_name, ctx) + return decorated + return decorator diff --git a/striker/profile/forms.py b/striker/profile/forms.py new file mode 100644 index 0000000..c446139 --- /dev/null +++ b/striker/profile/forms.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016 Wikimedia Foundation and contributors. +# All Rights Reserved. +# +# This file is part of Striker. +# +# Striker is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Striker is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Striker. If not, see <http://www.gnu.org/licenses/>. + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from striker.profile import utils + + +class SshKeyDeleteForm(forms.Form): + key_hash = forms.CharField( + label=_('SHA512 hash of ssh key'), + widget=forms.HiddenInput(), + required=True, + ) + + def __init__(self, request=None, *args, **kwargs): + self.request = request + self.cleaned_keys = None + super(SshKeyDeleteForm, self).__init__(*args, **kwargs) + + def clean(self): + key_hash = self.cleaned_data.get('key_hash') + hashes = utils.ssh_keys_by_hash(self.request.user) + if key_hash not in hashes: + raise forms.ValidationError( + _('SSH key not found.'), code='key_not_found') + del hashes[key_hash] + self.cleaned_keys = hashes.values() + return self.cleaned_data diff --git a/striker/profile/urls.py b/striker/profile/urls.py index 7dd680e..d2292ea 100644 --- a/striker/profile/urls.py +++ b/striker/profile/urls.py @@ -45,4 +45,9 @@ 'striker.profile.views.ssh_keys', name='ssh_keys' ), + urls.url( + r'^settings/ssh-keys/delete$', + 'striker.profile.views.ssh_key_delete', + name='ssh_key_delete' + ), ] diff --git a/striker/profile/utils.py b/striker/profile/utils.py index 9efc0cf..75b7ba5 100644 --- a/striker/profile/utils.py +++ b/striker/profile/utils.py @@ -49,3 +49,10 @@ key.type_name = key.key_type.decode('utf-8') return key + + +def ssh_keys_by_hash(user): + return { + parse_ssh_key(key).hash_sha256():key + for key in user.ldapuser.ssh_keys + } diff --git a/striker/profile/views.py b/striker/profile/views.py index 380c7c3..689710f 100644 --- a/striker/profile/views.py +++ b/striker/profile/views.py @@ -28,7 +28,9 @@ from django.db.utils import DatabaseError from django.utils.translation import ugettext_lazy as _ +from striker import decorators from striker import phabricator +from striker.profile import forms from striker.profile import utils @@ -80,4 +82,25 @@ ctx = { 'ssh_keys': [utils.parse_ssh_key(key) for key in ldapuser.ssh_keys], } + for key in ctx['ssh_keys']: + key.form = forms.SshKeyDeleteForm( + initial={'key_hash': key.hash_sha256()}) return shortcuts.render(req, 'profile/settings/ssh-keys.html', ctx) + + +@login_required +@decorators.confirm_required('profile/settings/ssh-keys/delete-confirm.html') +def ssh_key_delete(req): + if req.method == 'POST': + form = forms.SshKeyDeleteForm(data=req.POST, request=req) + if form.is_valid(): + key_hash = form.cleaned_data.get('key_hash') + ldapuser = req.user.ldapuser + ldapuser.ssh_keys = form.cleaned_keys + ldapuser.save() + messages.info( + req, + _("Deleted SSH key {key_hash}").format(key_hash=key_hash)) + else: + messages.error(req, _('Key not found.')) + return shortcuts.redirect(urlresolvers.reverse('profile:ssh_keys')) diff --git a/striker/templates/profile/settings/ssh-keys.html b/striker/templates/profile/settings/ssh-keys.html index e0ed52f..60c718b 100644 --- a/striker/templates/profile/settings/ssh-keys.html +++ b/striker/templates/profile/settings/ssh-keys.html @@ -1,4 +1,5 @@ {% extends "profile/settings/base.html" %} +{% load bootstrap3 %} {% load fontawesome %} {% load i18n %} @@ -8,7 +9,19 @@ {% for key in ssh_keys %} <div class="panel panel-default"> <div class="panel-heading"> - <h3 class="panel-title"><span class="fa-stack">{% fa_icon "square" "stack-2x" "fw" aria_hidden="true" %}{% fa_icon "key" "stack-1x" "fw" "inverse" aria_hidden="true" %}</span> {{ key.comment }} ({{ key.bits }} {{ key.type_name }})</h3> + <div class="row"> + <div class="col-sm-11"> + <h3 class="panel-title"><span class="fa-stack">{% fa_icon "square" "stack-2x" "fw" aria_hidden="true" %}{% fa_icon "key" "stack-1x" "fw" "inverse" aria_hidden="true" %}</span> {{ key.comment }} ({{ key.bits }} {{ key.type_name }})</h3> + </div> + <div class="col-sm-1"> + <form method="post" action="{% url 'profile:ssh_key_delete' %}" class="form form-inline"> + {% csrf_token %} + {% bootstrap_form key.form %} + {% trans "Delete" as delete_title %} + <button class="btn btn-danger pull-right" type="submit" title="{{ delete_title }}" aria-label="{{ delete_title }}" >{% bootstrap_icon "trash" %}</button> + </form> + </div> + </div> </div> <div class="panel-body"> <dl class="dl-horizontal"> diff --git a/striker/templates/profile/settings/ssh-keys/delete-confirm.html b/striker/templates/profile/settings/ssh-keys/delete-confirm.html new file mode 100644 index 0000000..e9a9061 --- /dev/null +++ b/striker/templates/profile/settings/ssh-keys/delete-confirm.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% load bootstrap3 %} +{% load i18n %} + +{% block title %}{% trans "Delete SSH key?" %}{% endblock %} +{% block content %} +<div class="row"> + <div class="col-sm-6 col-sm-offset-3"> + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">{% trans "Are you sure you want to delete this SSH key?" %}</h3> + </div> + <div class="panel-body text-center"> + <samp>{{ request.POST.key_hash }}</samp> + </div> + <div class="panel-body alert alert-warning" role="alert"> + {% blocktrans %}This action <strong>CANNOT</strong> be undone. This will permanently delete the SSH key and if you'd like to use it in the future, you will need to upload it again.{% endblocktrans %} + </div> + <div class="panel-body row"> + <div class="col-sm-6"> + <form method="post" action="{% url 'profile:ssh_key_delete' %}" class="form form-inline"> + {% csrf_token %} + <input type="hidden" name="__confirm__" value="1"> + <input type="hidden" name="key_hash" value="{{ request.POST.key_hash }}"> + <button class="btn btn-danger btn-block" type="submit"> + {% bootstrap_icon "trash" %} {% trans "Delete" %} + </button> + </form> + </div> + <div class="col-sm-6"> + <a href="{% url 'profile:ssh_keys' %}" class="btn btn-default btn-block"> + {% trans "Cancel" %} + </a> + </div> + </div> + </div> + </div> +</div> +{% endblock %} +{# vim:sw=2:ts=2:sts=2:et: #} -- To view, visit https://gerrit.wikimedia.org/r/328117 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1a307bf35fc6f68ef316c23ff037184d696aca42 Gerrit-PatchSet: 1 Gerrit-Project: labs/striker Gerrit-Branch: master Gerrit-Owner: BryanDavis <bda...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits