changeset 7771:3e84ac8ed579

ssh: admin management of ssh keys Based on work by Ilya Beda <ir4y.ix@gmail.com> on https://bitbucket.org/ir4y/rhodecode/commits/branch/ssh_server_support . Bootstrap support, updates for POST methods, and tests by Anton Schur <tonich.sh@gmail.com>. Additional Bootstrap fixes by Dominik Ruf. Also heavily modified by Mads Kiilerich.
author Christian Oyarzun <oyarzun@gmail.com>
date Mon, 17 Nov 2014 14:42:45 -0500
parents 6da70f4569bf
children 66c208bf56fe
files CONTRIBUTORS kallithea/config/routing.py kallithea/controllers/admin/users.py kallithea/templates/about.html kallithea/templates/admin/users/user_edit.html kallithea/templates/admin/users/user_edit_ssh_keys.html kallithea/tests/functional/test_admin_users.py scripts/contributor_data.py
diffstat 8 files changed, 161 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/CONTRIBUTORS	Fri Jul 19 01:12:35 2019 +0200
+++ b/CONTRIBUTORS	Mon Nov 17 14:42:45 2014 -0500
@@ -97,6 +97,7 @@
     Aparkar <aparkar@icloud.com> 2013
     Dennis Brakhane <brakhane@googlemail.com> 2013
     Grzegorz Rożniecki <xaerxess@gmail.com> 2013
+    Ilya Beda <ir4y.ix@gmail.com> 2013
     Jonathan Sternberg <jonathansternberg@gmail.com> 2013
     Leonardo Carneiro <leonardo@unity3d.com> 2013
     Magnus Ericmats <magnus.ericmats@gmail.com> 2013
--- a/kallithea/config/routing.py	Fri Jul 19 01:12:35 2019 +0200
+++ b/kallithea/config/routing.py	Mon Nov 17 14:42:45 2014 -0500
@@ -183,6 +183,13 @@
         m.connect("edit_user_api_keys_delete", "/users/{id}/edit/api_keys/delete",
                   action="delete_api_key", conditions=dict(method=["POST"]))
 
+        m.connect("edit_user_ssh_keys", "/users/{id}/edit/ssh_keys",
+                  action="edit_ssh_keys", conditions=dict(method=["GET"]))
+        m.connect("edit_user_ssh_keys", "/users/{id}/edit/ssh_keys",
+                  action="ssh_keys_add", conditions=dict(method=["POST"]))
+        m.connect("edit_user_ssh_keys_delete", "/users/{id}/edit/ssh_keys/delete",
+                  action="ssh_keys_delete", conditions=dict(method=["POST"]))
+
         m.connect("edit_user_perms", "/users/{id}/edit/permissions",
                   action="edit_perms", conditions=dict(method=["GET"]))
         m.connect("edit_user_perms_update", "/users/{id}/edit/permissions",
--- a/kallithea/controllers/admin/users.py	Fri Jul 19 01:12:35 2019 +0200
+++ b/kallithea/controllers/admin/users.py	Mon Nov 17 14:42:45 2014 -0500
@@ -43,9 +43,9 @@
 from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator, \
     AuthUser
 from kallithea.lib import auth_modules
-from kallithea.lib.base import BaseController, render
+from kallithea.lib.base import BaseController, render, IfSshEnabled
 from kallithea.model.api_key import ApiKeyModel
-
+from kallithea.model.ssh_key import SshKeyModel
 from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm
 from kallithea.model.forms import UserForm, CustomDefaultPermissionsForm
 from kallithea.model.user import UserModel
@@ -429,3 +429,37 @@
         if 'default_user' in request.POST:
             raise HTTPFound(location=url('admin_permissions_ips'))
         raise HTTPFound(location=url('edit_user_ips', id=id))
+
+    @IfSshEnabled
+    def edit_ssh_keys(self, id):
+        c.user = self._get_user_or_raise_if_default(id)
+        c.active = 'ssh_keys'
+        c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
+        defaults = c.user.get_dict()
+        return htmlfill.render(
+            render('admin/users/user_edit.html'),
+            defaults=defaults,
+            encoding="UTF-8",
+            force_defaults=False)
+
+    @IfSshEnabled
+    def ssh_keys_add(self, id):
+        c.user = self._get_user_or_raise_if_default(id)
+
+        description = request.POST.get('description')
+        public_key = request.POST.get('public_key')
+        new_ssh_key = SshKeyModel().create(c.user.user_id,
+                                       description, public_key)
+        Session().commit()
+        h.flash(_("SSH key %s successfully added") % new_ssh_key.fingerprint, category='success')
+        raise HTTPFound(location=url('edit_user_ssh_keys', id=c.user.user_id))
+
+    @IfSshEnabled
+    def ssh_keys_delete(self, id):
+        c.user = self._get_user_or_raise_if_default(id)
+
+        public_key = request.POST.get('del_public_key')
+        SshKeyModel().delete(public_key, c.user.user_id)
+        Session().commit()
+        h.flash(_("SSH key successfully deleted"), category='success')
+        raise HTTPFound(location=url('edit_user_ssh_keys', id=c.user.user_id))
--- a/kallithea/templates/about.html	Fri Jul 19 01:12:35 2019 +0200
+++ b/kallithea/templates/about.html	Mon Nov 17 14:42:45 2014 -0500
@@ -121,6 +121,7 @@
   <li>Copyright &copy; 2012&ndash;2013, xpol</li>
   <li>Copyright &copy; 2013, Dennis Brakhane</li>
   <li>Copyright &copy; 2013, Grzegorz Rożniecki</li>
+  <li>Copyright &copy; 2013, Ilya Beda</li>
   <li>Copyright &copy; 2013, Jonathan Sternberg</li>
   <li>Copyright &copy; 2013, Leonardo Carneiro</li>
   <li>Copyright &copy; 2013, Magnus Ericmats</li>
--- a/kallithea/templates/admin/users/user_edit.html	Fri Jul 19 01:12:35 2019 +0200
+++ b/kallithea/templates/admin/users/user_edit.html	Mon Nov 17 14:42:45 2014 -0500
@@ -28,6 +28,9 @@
       <ul class="nav nav-pills nav-stacked">
         <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', id=c.user.user_id)}">${_('Profile')}</a></li>
         <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', id=c.user.user_id)}">${_('Emails')}</a></li>
+        %if c.ssh_enabled:
+          <li class="${'active' if c.active=='ssh_keys' else ''}"><a href="${h.url('edit_user_ssh_keys', id=c.user.user_id)}">${_('SSH Keys')}</a></li>
+        %endif
         <li class="${'active' if c.active=='api_keys' else ''}"><a href="${h.url('edit_user_api_keys', id=c.user.user_id)}">${_('API Keys')}</a></li>
         <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', id=c.user.user_id)}">${_('IP Whitelist')}</a></li>
         <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', id=c.user.user_id)}">${_('Advanced')}</a></li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/templates/admin/users/user_edit_ssh_keys.html	Mon Nov 17 14:42:45 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('edit_user_ssh_keys_delete', id=c.user.user_id))}
+                    ${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('edit_user_ssh_keys', id=c.user.user_id))}
+    <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_admin_users.py	Fri Jul 19 01:12:35 2019 +0200
+++ b/kallithea/tests/functional/test_admin_users.py	Mon Nov 17 14:42:45 2014 -0500
@@ -18,7 +18,7 @@
 from kallithea.tests.base import *
 from kallithea.tests.fixture import Fixture
 from kallithea.controllers.admin.users import UsersController
-from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys, RepoGroup
+from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys, RepoGroup, UserSshKeys
 from kallithea.lib.auth import check_password
 from kallithea.model.user import UserModel
 from kallithea.model import validators
@@ -514,6 +514,54 @@
         response = response.follow()
         response.mustcontain(no=[api_key])
 
+    def test_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()
+        user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
+        user_id = user.user_id
+
+        response = self.app.post(url('edit_user_ssh_keys', id=user_id),
+                                 {'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)
+        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_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()
+        user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
+        user_id = user.user_id
+
+        response = self.app.post(url('edit_user_ssh_keys', id=user_id),
+                                 {'description': description,
+                                  'public_key': public_key,
+                                  '_authentication_token': self.authentication_token()})
+        self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint)
+        response.follow()
+        ssh_key = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).one()
+        assert ssh_key.description == description
+
+        response = self.app.post(url('edit_user_ssh_keys_delete', id=user_id),
+                                 {'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)
+
 
 class TestAdminUsersController_unittest(TestController):
     """ Unit tests for the users controller """
--- a/scripts/contributor_data.py	Fri Jul 19 01:12:35 2019 +0200
+++ b/scripts/contributor_data.py	Mon Nov 17 14:42:45 2014 -0500
@@ -66,6 +66,7 @@
 # history:
 other = [
     # Work folded into commits attributed to others:
+    ('2013', 'Ilya Beda <ir4y.ix@gmail.com>'),
 ]
 
 # Preserve contributors listed in about.html but not appearing in repository