diff --git a/kallithea/config/routing.py b/kallithea/config/routing.py --- a/kallithea/config/routing.py +++ b/kallithea/config/routing.py @@ -358,6 +358,13 @@ def make_map(config): 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: diff --git a/kallithea/controllers/admin/my_account.py b/kallithea/controllers/admin/my_account.py --- a/kallithea/controllers/admin/my_account.py +++ b/kallithea/controllers/admin/my_account.py @@ -39,13 +39,14 @@ from kallithea.config.routing import url 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 @@ class MyAccountController(BaseController 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')) diff --git a/kallithea/templates/admin/my_account/my_account.html b/kallithea/templates/admin/my_account/my_account.html --- a/kallithea/templates/admin/my_account/my_account.html +++ b/kallithea/templates/admin/my_account/my_account.html @@ -25,6 +25,9 @@
  • ${_('Profile')}
  • ${_('Email Addresses')}
  • ${_('Password')}
  • + %if c.ssh_enabled: +
  • ${_('SSH Keys')}
  • + %endif
  • ${_('API Keys')}
  • ${_('Owned Repositories')}
  • ${_('Watched Repositories')}
  • diff --git a/kallithea/templates/admin/my_account/my_account_ssh_keys.html b/kallithea/templates/admin/my_account/my_account_ssh_keys.html new file mode 100644 --- /dev/null +++ b/kallithea/templates/admin/my_account/my_account_ssh_keys.html @@ -0,0 +1,63 @@ + + %if c.user_ssh_keys: + + + + + + %for ssh_key in c.user_ssh_keys: + + + + + + %endfor + %else: + + + + %endif +
    ${_('Fingerprint')}${_('Description')}${_('Action')}
    + ${ssh_key.fingerprint} + + ${ssh_key.description} + + ${h.form(url('my_account_ssh_keys_delete'))} + ${h.hidden('del_public_key', ssh_key.public_key)} + + ${h.end_form()} +
    +
    ${_('No SSH keys have been added')}
    +
    + +
    + ${h.form(url('my_account_ssh_keys'))} +
    +
    + +
    +
    + +
    + ${h.textarea('public_key', '', class_='form-control', placeholder=_('Public key (contents of e.g. ~/.ssh/id_rsa.pub)'), cols=80, rows=5)} +
    +
    +
    + +
    + ${h.text('description', class_='form-control', placeholder=_('Description'))} +
    +
    +
    +
    + ${h.submit('save', _('Add'), class_="btn btn-default")} + ${h.reset('reset', _('Reset'), class_="btn btn-default")} +
    +
    +
    + ${h.end_form()} +
    diff --git a/kallithea/tests/functional/test_my_account.py b/kallithea/tests/functional/test_my_account.py --- a/kallithea/tests/functional/test_my_account.py +++ b/kallithea/tests/functional/test_my_account.py @@ -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 @@ class TestMyAccountController(TestContro 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)