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

Reply via email to