Changeset - 3e84ac8ed579
[Not reviewed]
default
0 7 1
Christian Oyarzun - 11 years ago 2014-11-17 20:42:45
oyarzun@gmail.com
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.
8 files changed with 161 insertions and 3 deletions:
0 comments (0 inline, 0 general)
CONTRIBUTORS
Show inline comments
 
@@ -94,12 +94,13 @@ List of contributors to Kallithea projec
 
    Zoltan Gyarmati <mr.zoltan.gyarmati@gmail.com> 2014
 
    Marcin Kuźmiński <marcin@python-works.com> 2010-2013
 
    xpol <xpolife@gmail.com> 2012-2013
 
    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
 
    Martin Vium <martinv@unity3d.com> 2013
 
    Simon Lopez <simon.lopez@slopez.org> 2013
 
    Ton Plomp <tcplomp@gmail.com> 2013
kallithea/config/routing.py
Show inline comments
 
@@ -180,12 +180,19 @@ def make_map(config):
 
                  action="edit_api_keys", conditions=dict(method=["GET"]))
 
        m.connect("edit_user_api_keys_update", "/users/{id}/edit/api_keys",
 
                  action="add_api_key", conditions=dict(method=["POST"]))
 
        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",
 
                  action="update_perms", conditions=dict(method=["POST"]))
 

	
 
        m.connect("edit_user_emails", "/users/{id}/edit/emails",
kallithea/controllers/admin/users.py
Show inline comments
 
@@ -40,15 +40,15 @@ from kallithea.config.routing import url
 
from kallithea.lib.exceptions import DefaultUserException, \
 
    UserOwnsReposException, UserCreationError
 
from kallithea.lib import helpers as h
 
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
 
from kallithea.model.meta import Session
 
from kallithea.lib.utils import action_logger
 
from kallithea.lib.utils2 import datetime_to_time, safe_int, generate_api_key
 
@@ -426,6 +426,40 @@ class UsersController(BaseController):
 
        Session().commit()
 
        h.flash(_("Removed IP address from user whitelist"), category='success')
 

	
 
        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))
kallithea/templates/about.html
Show inline comments
 
@@ -118,12 +118,13 @@
 
  <li>Copyright &copy; 2010&ndash;2013, Marcin Kuźmiński</li>
 
  <li>Copyright &copy; 2010&ndash;2013, RhodeCode GmbH</li>
 
  <li>Copyright &copy; 2011, 2013, Aparkar</li>
 
  <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>
 
  <li>Copyright &copy; 2013, Martin Vium</li>
 
  <li>Copyright &copy; 2013, Simon Lopez</li>
 
  <li>Copyright &copy; 2011&ndash;2012, Augusto Herrmann</li>
kallithea/templates/admin/users/user_edit.html
Show inline comments
 
@@ -25,12 +25,15 @@
 

	
 
    ##main
 
    <div class="panel-body settings">
 
      <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>
 
        <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_user_perms', id=c.user.user_id)}">${_('Show Permissions')}</a></li>
 
      </ul>
 

	
kallithea/templates/admin/users/user_edit_ssh_keys.html
Show inline comments
 
new file 100644
 
<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>
kallithea/tests/functional/test_admin_users.py
Show inline comments
 
@@ -15,13 +15,13 @@
 
from sqlalchemy.orm.exc import NoResultFound, ObjectDeletedError
 

	
 
import pytest
 
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
 
from kallithea.lib import helpers as h
 
from kallithea.model.meta import Session
 
from webob.exc import HTTPNotFound
 
@@ -511,12 +511,60 @@ class TestAdminUsersController(TestContr
 
        response = self.app.post(url('edit_user_api_keys_delete', id=user_id),
 
                 {'del_api_key_builtin': api_key, '_authentication_token': self.authentication_token()})
 
        self.checkSessionFlash(response, 'API key successfully reset')
 
        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 """
 

	
 
    def test_get_user_or_raise_if_default(self, monkeypatch, test_context_fixture):
 
        # flash complains about an non-existing session
scripts/contributor_data.py
Show inline comments
 
@@ -63,12 +63,13 @@ no_about.add(('Ton Plomp <tcplomp@gmail.
 
no_about.add(('Sean Farley <sean.michael.farley@gmail.com>', '2013'))
 

	
 
# Contributors in about.html and CONTRIBUTORS not appearing in repository
 
# 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
 
# history:
 
other_about = [
 
    ("2011", "Aparkar <aparkar@icloud.com>"),
0 comments (0 inline, 0 general)