changeset 7772:66c208bf56fe

ssh: user management of ssh keys Add user interface for managing SSH based access. The work in this commit is based heavily off of the existing API key code for the sake of consistency. Updates to use Bootstrap, request.authuser, POST methods and pytest by Anton Schur <tonich.sh@gmail.com>. Additional Bootstrap fixes by Dominik Ruf. The original code has been heavily modified by Mads Kiilerich.
author Tim Freund <tim@freunds.net>
date Mon, 17 Nov 2014 14:40:35 -0500
parents 3e84ac8ed579
children 3b147c38b674
files kallithea/config/routing.py kallithea/controllers/admin/my_account.py kallithea/templates/admin/my_account/my_account.html kallithea/templates/admin/my_account/my_account_ssh_keys.html kallithea/tests/functional/test_my_account.py
diffstat 5 files changed, 145 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/config/routing.py	Mon Nov 17 14:42:45 2014 -0500
+++ b/kallithea/config/routing.py	Mon Nov 17 14:40:35 2014 -0500
@@ -358,6 +358,13 @@
         m.connect("my_account_api_keys_delete", "/my_account/api_keys/delete",
                   action="my_account_api_keys_delete", conditions=dict(method=["POST"]))
 
+        m.connect("my_account_ssh_keys", "/my_account/ssh_keys",
+                  action="my_account_ssh_keys", conditions=dict(method=["GET"]))
+        m.connect("my_account_ssh_keys", "/my_account/ssh_keys",
+                  action="my_account_ssh_keys_add", conditions=dict(method=["POST"]))
+        m.connect("my_account_ssh_keys_delete", "/my_account/ssh_keys/delete",
+                  action="my_account_ssh_keys_delete", conditions=dict(method=["POST"]))
+
     # ADMIN GIST
     with rmap.submapper(path_prefix=ADMIN_PREFIX,
                         controller='admin/gists') as m:
--- a/kallithea/controllers/admin/my_account.py	Mon Nov 17 14:42:45 2014 -0500
+++ b/kallithea/controllers/admin/my_account.py	Mon Nov 17 14:40:35 2014 -0500
@@ -39,13 +39,14 @@
 from kallithea.lib import helpers as h
 from kallithea.lib import auth_modules
 from kallithea.lib.auth import LoginRequired, AuthUser
-from kallithea.lib.base import BaseController, render
+from kallithea.lib.base import BaseController, render, IfSshEnabled
 from kallithea.lib.utils2 import generate_api_key, safe_int
 from kallithea.model.db import Repository, UserEmailMap, User, UserFollowing
 from kallithea.model.forms import UserForm, PasswordChangeForm
 from kallithea.model.user import UserModel
 from kallithea.model.repo import RepoModel
 from kallithea.model.api_key import ApiKeyModel
+from kallithea.model.ssh_key import SshKeyModel
 from kallithea.model.meta import Session
 
 log = logging.getLogger(__name__)
@@ -259,3 +260,28 @@
             h.flash(_("API key successfully deleted"), category='success')
 
         raise HTTPFound(location=url('my_account_api_keys'))
+
+    @IfSshEnabled
+    def my_account_ssh_keys(self):
+        c.active = 'ssh_keys'
+        self.__load_data()
+        c.user_ssh_keys = SshKeyModel().get_ssh_keys(request.authuser.user_id)
+        return render('admin/my_account/my_account.html')
+
+    @IfSshEnabled
+    def my_account_ssh_keys_add(self):
+        description = request.POST.get('description')
+        public_key = request.POST.get('public_key')
+        new_ssh_key = SshKeyModel().create(request.authuser.user_id,
+                                           description, public_key)
+        Session().commit()
+        h.flash(_("SSH key %s successfully added") % new_ssh_key.fingerprint, category='success')
+        raise HTTPFound(location=url('my_account_ssh_keys'))
+
+    @IfSshEnabled
+    def my_account_ssh_keys_delete(self):
+        public_key = request.POST.get('del_public_key')
+        SshKeyModel().delete(public_key, request.authuser.user_id)
+        Session().commit()
+        h.flash(_("SSH key successfully deleted"), category='success')
+        raise HTTPFound(location=url('my_account_ssh_keys'))
--- a/kallithea/templates/admin/my_account/my_account.html	Mon Nov 17 14:42:45 2014 -0500
+++ b/kallithea/templates/admin/my_account/my_account.html	Mon Nov 17 14:40:35 2014 -0500
@@ -25,6 +25,9 @@
             <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('my_account')}">${_('Profile')}</a></li>
             <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('my_account_emails')}">${_('Email Addresses')}</a></li>
             <li class="${'active' if c.active=='password' else ''}"><a href="${h.url('my_account_password')}">${_('Password')}</a></li>
+            %if c.ssh_enabled:
+              <li class="${'active' if c.active=='ssh_keys' else ''}"><a href="${h.url('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
+            %endif
             <li class="${'active' if c.active=='api_keys' else ''}"><a href="${h.url('my_account_api_keys')}">${_('API Keys')}</a></li>
             <li class="${'active' if c.active=='repos' else ''}"><a href="${h.url('my_account_repos')}">${_('Owned Repositories')}</a></li>
             <li class="${'active' if c.active=='watched' else ''}"><a href="${h.url('my_account_watched')}">${_('Watched Repositories')}</a></li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/templates/admin/my_account/my_account_ssh_keys.html	Mon Nov 17 14:40:35 2014 -0500
@@ -0,0 +1,63 @@
+<table class="table">
+    %if c.user_ssh_keys:
+        <tr>
+            <th>${_('Fingerprint')}</th>
+            <th>${_('Description')}</th>
+            <th>${_('Action')}</th>
+        </tr>
+        %for ssh_key in c.user_ssh_keys:
+          <tr>
+            <td>
+                ${ssh_key.fingerprint}
+            </td>
+            <td>
+                ${ssh_key.description}
+            </td>
+            <td>
+                ${h.form(url('my_account_ssh_keys_delete'))}
+                    ${h.hidden('del_public_key', ssh_key.public_key)}
+                    <button class="btn btn-danger btn-xs" type="submit"
+                            onclick="return confirm('${_('Confirm to remove this SSH key: %s') % ssh_key.fingerprint}');">
+                        <i class="icon-trashcan"></i>
+                        ${_('Remove')}
+                    </button>
+                ${h.end_form()}
+            </td>
+          </tr>
+        %endfor
+    %else:
+        <tr>
+            <td>
+                <div class="ip">${_('No SSH keys have been added')}</div>
+            </td>
+        </tr>
+    %endif
+</table>
+
+<div>
+    ${h.form(url('my_account_ssh_keys'))}
+    <div class="form">
+            <div class="form-group">
+                <label class="control-label">${_('New SSH key')}</label>
+            </div>
+            <div class="form-group">
+                <label class="control-label" for="public_key">${_('Public key')}:</label>
+                <div>
+                    ${h.textarea('public_key', '', class_='form-control', placeholder=_('Public key (contents of e.g. ~/.ssh/id_rsa.pub)'), cols=80, rows=5)}
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label" for="description">${_('Description')}:</label>
+                <div>
+                    ${h.text('description', class_='form-control', placeholder=_('Description'))}
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="buttons">
+                    ${h.submit('save', _('Add'), class_="btn btn-default")}
+                    ${h.reset('reset', _('Reset'), class_="btn btn-default")}
+                </div>
+            </div>
+    </div>
+    ${h.end_form()}
+</div>
--- a/kallithea/tests/functional/test_my_account.py	Mon Nov 17 14:42:45 2014 -0500
+++ b/kallithea/tests/functional/test_my_account.py	Mon Nov 17 14:40:35 2014 -0500
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 
-from kallithea.model.db import User, UserFollowing, Repository, UserApiKeys
+from kallithea.model.db import User, UserFollowing, Repository, UserApiKeys, UserSshKeys
 from kallithea.tests.base import *
 from kallithea.tests.fixture import Fixture
 from kallithea.lib import helpers as h
@@ -249,3 +249,47 @@
         self.checkSessionFlash(response, 'API key successfully reset')
         response = response.follow()
         response.mustcontain(no=[api_key])
+
+    def test_my_account_add_ssh_key(self):
+        description = u'something'
+        public_key = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost'
+        fingerprint = u'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8'
+
+        self.log_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS)
+        response = self.app.post(url('my_account_ssh_keys'),
+                                 {'description': description,
+                                  'public_key': public_key,
+                                  '_authentication_token': self.authentication_token()})
+        self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint)
+
+        response = response.follow()
+        response.mustcontain(fingerprint)
+        user_id = response.session['authuser']['user_id']
+        ssh_key = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).one()
+        assert ssh_key.fingerprint == fingerprint
+        assert ssh_key.description == description
+        Session().delete(ssh_key)
+        Session().commit()
+
+    def test_my_account_remove_ssh_key(self):
+        description = u''
+        public_key = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost'
+        fingerprint = u'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8'
+
+        self.log_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS)
+        response = self.app.post(url('my_account_ssh_keys'),
+                                 {'description': description,
+                                  'public_key': public_key,
+                                  '_authentication_token': self.authentication_token()})
+        self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint)
+        response.follow()
+        user_id = response.session['authuser']['user_id']
+        ssh_key = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).one()
+        assert ssh_key.description == description
+
+        response = self.app.post(url('my_account_ssh_keys_delete'),
+                                 {'del_public_key': ssh_key.public_key,
+                                  '_authentication_token': self.authentication_token()})
+        self.checkSessionFlash(response, 'SSH key successfully deleted')
+        keys = UserSshKeys.query().all()
+        assert 0 == len(keys)