Show More
@@ -0,0 +1,63 b'' | |||||
|
1 | <table class="table"> | |||
|
2 | %if c.user_ssh_keys: | |||
|
3 | <tr> | |||
|
4 | <th>${_('Fingerprint')}</th> | |||
|
5 | <th>${_('Description')}</th> | |||
|
6 | <th>${_('Action')}</th> | |||
|
7 | </tr> | |||
|
8 | %for ssh_key in c.user_ssh_keys: | |||
|
9 | <tr> | |||
|
10 | <td> | |||
|
11 | ${ssh_key.fingerprint} | |||
|
12 | </td> | |||
|
13 | <td> | |||
|
14 | ${ssh_key.description} | |||
|
15 | </td> | |||
|
16 | <td> | |||
|
17 | ${h.form(url('edit_user_ssh_keys_delete', id=c.user.user_id))} | |||
|
18 | ${h.hidden('del_public_key', ssh_key.public_key)} | |||
|
19 | <button class="btn btn-danger btn-xs" type="submit" | |||
|
20 | onclick="return confirm('${_('Confirm to remove this SSH key: %s') % ssh_key.fingerprint}');"> | |||
|
21 | <i class="icon-trashcan"></i> | |||
|
22 | ${_('Remove')} | |||
|
23 | </button> | |||
|
24 | ${h.end_form()} | |||
|
25 | </td> | |||
|
26 | </tr> | |||
|
27 | %endfor | |||
|
28 | %else: | |||
|
29 | <tr> | |||
|
30 | <td> | |||
|
31 | <div class="ip">${_('No SSH keys have been added')}</div> | |||
|
32 | </td> | |||
|
33 | </tr> | |||
|
34 | %endif | |||
|
35 | </table> | |||
|
36 | ||||
|
37 | <div> | |||
|
38 | ${h.form(url('edit_user_ssh_keys', id=c.user.user_id))} | |||
|
39 | <div class="form"> | |||
|
40 | <div class="form-group"> | |||
|
41 | <label class="control-label">${_('New SSH key')}</label> | |||
|
42 | </div> | |||
|
43 | <div class="form-group"> | |||
|
44 | <label class="control-label" for="public_key">${_('Public key')}:</label> | |||
|
45 | <div> | |||
|
46 | ${h.textarea('public_key', '', class_='form-control', placeholder=_('Public key (contents of e.g. ~/.ssh/id_rsa.pub)'), cols=80, rows=5)} | |||
|
47 | </div> | |||
|
48 | </div> | |||
|
49 | <div class="form-group"> | |||
|
50 | <label class="control-label" for="description">${_('Description')}:</label> | |||
|
51 | <div> | |||
|
52 | ${h.text('description', class_='form-control', placeholder=_('Description'))} | |||
|
53 | </div> | |||
|
54 | </div> | |||
|
55 | <div class="form-group"> | |||
|
56 | <div class="buttons"> | |||
|
57 | ${h.submit('save', _('Add'), class_="btn btn-default")} | |||
|
58 | ${h.reset('reset', _('Reset'), class_="btn btn-default")} | |||
|
59 | </div> | |||
|
60 | </div> | |||
|
61 | </div> | |||
|
62 | ${h.end_form()} | |||
|
63 | </div> |
@@ -97,6 +97,7 b' List of contributors to Kallithea projec' | |||||
97 | Aparkar <aparkar@icloud.com> 2013 |
|
97 | Aparkar <aparkar@icloud.com> 2013 | |
98 | Dennis Brakhane <brakhane@googlemail.com> 2013 |
|
98 | Dennis Brakhane <brakhane@googlemail.com> 2013 | |
99 | Grzegorz Rożniecki <xaerxess@gmail.com> 2013 |
|
99 | Grzegorz Rożniecki <xaerxess@gmail.com> 2013 | |
|
100 | Ilya Beda <ir4y.ix@gmail.com> 2013 | |||
100 | Jonathan Sternberg <jonathansternberg@gmail.com> 2013 |
|
101 | Jonathan Sternberg <jonathansternberg@gmail.com> 2013 | |
101 | Leonardo Carneiro <leonardo@unity3d.com> 2013 |
|
102 | Leonardo Carneiro <leonardo@unity3d.com> 2013 | |
102 | Magnus Ericmats <magnus.ericmats@gmail.com> 2013 |
|
103 | Magnus Ericmats <magnus.ericmats@gmail.com> 2013 |
@@ -183,6 +183,13 b' def make_map(config):' | |||||
183 | m.connect("edit_user_api_keys_delete", "/users/{id}/edit/api_keys/delete", |
|
183 | m.connect("edit_user_api_keys_delete", "/users/{id}/edit/api_keys/delete", | |
184 | action="delete_api_key", conditions=dict(method=["POST"])) |
|
184 | action="delete_api_key", conditions=dict(method=["POST"])) | |
185 |
|
185 | |||
|
186 | m.connect("edit_user_ssh_keys", "/users/{id}/edit/ssh_keys", | |||
|
187 | action="edit_ssh_keys", conditions=dict(method=["GET"])) | |||
|
188 | m.connect("edit_user_ssh_keys", "/users/{id}/edit/ssh_keys", | |||
|
189 | action="ssh_keys_add", conditions=dict(method=["POST"])) | |||
|
190 | m.connect("edit_user_ssh_keys_delete", "/users/{id}/edit/ssh_keys/delete", | |||
|
191 | action="ssh_keys_delete", conditions=dict(method=["POST"])) | |||
|
192 | ||||
186 | m.connect("edit_user_perms", "/users/{id}/edit/permissions", |
|
193 | m.connect("edit_user_perms", "/users/{id}/edit/permissions", | |
187 | action="edit_perms", conditions=dict(method=["GET"])) |
|
194 | action="edit_perms", conditions=dict(method=["GET"])) | |
188 | m.connect("edit_user_perms_update", "/users/{id}/edit/permissions", |
|
195 | m.connect("edit_user_perms_update", "/users/{id}/edit/permissions", |
@@ -43,9 +43,9 b' from kallithea.lib import helpers as h' | |||||
43 | from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator, \ |
|
43 | from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator, \ | |
44 | AuthUser |
|
44 | AuthUser | |
45 | from kallithea.lib import auth_modules |
|
45 | from kallithea.lib import auth_modules | |
46 | from kallithea.lib.base import BaseController, render |
|
46 | from kallithea.lib.base import BaseController, render, IfSshEnabled | |
47 | from kallithea.model.api_key import ApiKeyModel |
|
47 | from kallithea.model.api_key import ApiKeyModel | |
48 |
|
48 | from kallithea.model.ssh_key import SshKeyModel | ||
49 | from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm |
|
49 | from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm | |
50 | from kallithea.model.forms import UserForm, CustomDefaultPermissionsForm |
|
50 | from kallithea.model.forms import UserForm, CustomDefaultPermissionsForm | |
51 | from kallithea.model.user import UserModel |
|
51 | from kallithea.model.user import UserModel | |
@@ -429,3 +429,37 b' class UsersController(BaseController):' | |||||
429 | if 'default_user' in request.POST: |
|
429 | if 'default_user' in request.POST: | |
430 | raise HTTPFound(location=url('admin_permissions_ips')) |
|
430 | raise HTTPFound(location=url('admin_permissions_ips')) | |
431 | raise HTTPFound(location=url('edit_user_ips', id=id)) |
|
431 | raise HTTPFound(location=url('edit_user_ips', id=id)) | |
|
432 | ||||
|
433 | @IfSshEnabled | |||
|
434 | def edit_ssh_keys(self, id): | |||
|
435 | c.user = self._get_user_or_raise_if_default(id) | |||
|
436 | c.active = 'ssh_keys' | |||
|
437 | c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id) | |||
|
438 | defaults = c.user.get_dict() | |||
|
439 | return htmlfill.render( | |||
|
440 | render('admin/users/user_edit.html'), | |||
|
441 | defaults=defaults, | |||
|
442 | encoding="UTF-8", | |||
|
443 | force_defaults=False) | |||
|
444 | ||||
|
445 | @IfSshEnabled | |||
|
446 | def ssh_keys_add(self, id): | |||
|
447 | c.user = self._get_user_or_raise_if_default(id) | |||
|
448 | ||||
|
449 | description = request.POST.get('description') | |||
|
450 | public_key = request.POST.get('public_key') | |||
|
451 | new_ssh_key = SshKeyModel().create(c.user.user_id, | |||
|
452 | description, public_key) | |||
|
453 | Session().commit() | |||
|
454 | h.flash(_("SSH key %s successfully added") % new_ssh_key.fingerprint, category='success') | |||
|
455 | raise HTTPFound(location=url('edit_user_ssh_keys', id=c.user.user_id)) | |||
|
456 | ||||
|
457 | @IfSshEnabled | |||
|
458 | def ssh_keys_delete(self, id): | |||
|
459 | c.user = self._get_user_or_raise_if_default(id) | |||
|
460 | ||||
|
461 | public_key = request.POST.get('del_public_key') | |||
|
462 | SshKeyModel().delete(public_key, c.user.user_id) | |||
|
463 | Session().commit() | |||
|
464 | h.flash(_("SSH key successfully deleted"), category='success') | |||
|
465 | raise HTTPFound(location=url('edit_user_ssh_keys', id=c.user.user_id)) |
@@ -121,6 +121,7 b'' | |||||
121 | <li>Copyright © 2012–2013, xpol</li> |
|
121 | <li>Copyright © 2012–2013, xpol</li> | |
122 | <li>Copyright © 2013, Dennis Brakhane</li> |
|
122 | <li>Copyright © 2013, Dennis Brakhane</li> | |
123 | <li>Copyright © 2013, Grzegorz Rożniecki</li> |
|
123 | <li>Copyright © 2013, Grzegorz Rożniecki</li> | |
|
124 | <li>Copyright © 2013, Ilya Beda</li> | |||
124 | <li>Copyright © 2013, Jonathan Sternberg</li> |
|
125 | <li>Copyright © 2013, Jonathan Sternberg</li> | |
125 | <li>Copyright © 2013, Leonardo Carneiro</li> |
|
126 | <li>Copyright © 2013, Leonardo Carneiro</li> | |
126 | <li>Copyright © 2013, Magnus Ericmats</li> |
|
127 | <li>Copyright © 2013, Magnus Ericmats</li> |
@@ -28,6 +28,9 b'' | |||||
28 | <ul class="nav nav-pills nav-stacked"> |
|
28 | <ul class="nav nav-pills nav-stacked"> | |
29 | <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', id=c.user.user_id)}">${_('Profile')}</a></li> |
|
29 | <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', id=c.user.user_id)}">${_('Profile')}</a></li> | |
30 | <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', id=c.user.user_id)}">${_('Emails')}</a></li> |
|
30 | <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', id=c.user.user_id)}">${_('Emails')}</a></li> | |
|
31 | %if c.ssh_enabled: | |||
|
32 | <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> | |||
|
33 | %endif | |||
31 | <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> |
|
34 | <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> | |
32 | <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', id=c.user.user_id)}">${_('IP Whitelist')}</a></li> |
|
35 | <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', id=c.user.user_id)}">${_('IP Whitelist')}</a></li> | |
33 | <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', id=c.user.user_id)}">${_('Advanced')}</a></li> |
|
36 | <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', id=c.user.user_id)}">${_('Advanced')}</a></li> |
@@ -18,7 +18,7 b' import pytest' | |||||
18 | from kallithea.tests.base import * |
|
18 | from kallithea.tests.base import * | |
19 | from kallithea.tests.fixture import Fixture |
|
19 | from kallithea.tests.fixture import Fixture | |
20 | from kallithea.controllers.admin.users import UsersController |
|
20 | from kallithea.controllers.admin.users import UsersController | |
21 | from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys, RepoGroup |
|
21 | from kallithea.model.db import User, Permission, UserIpMap, UserApiKeys, RepoGroup, UserSshKeys | |
22 | from kallithea.lib.auth import check_password |
|
22 | from kallithea.lib.auth import check_password | |
23 | from kallithea.model.user import UserModel |
|
23 | from kallithea.model.user import UserModel | |
24 | from kallithea.model import validators |
|
24 | from kallithea.model import validators | |
@@ -514,6 +514,54 b' class TestAdminUsersController(TestContr' | |||||
514 | response = response.follow() |
|
514 | response = response.follow() | |
515 | response.mustcontain(no=[api_key]) |
|
515 | response.mustcontain(no=[api_key]) | |
516 |
|
516 | |||
|
517 | def test_add_ssh_key(self): | |||
|
518 | description = u'something' | |||
|
519 | public_key = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost' | |||
|
520 | fingerprint = u'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8' | |||
|
521 | ||||
|
522 | self.log_user() | |||
|
523 | user = User.get_by_username(TEST_USER_REGULAR_LOGIN) | |||
|
524 | user_id = user.user_id | |||
|
525 | ||||
|
526 | response = self.app.post(url('edit_user_ssh_keys', id=user_id), | |||
|
527 | {'description': description, | |||
|
528 | 'public_key': public_key, | |||
|
529 | '_authentication_token': self.authentication_token()}) | |||
|
530 | self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint) | |||
|
531 | ||||
|
532 | response = response.follow() | |||
|
533 | response.mustcontain(fingerprint) | |||
|
534 | ssh_key = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).one() | |||
|
535 | assert ssh_key.fingerprint == fingerprint | |||
|
536 | assert ssh_key.description == description | |||
|
537 | Session().delete(ssh_key) | |||
|
538 | Session().commit() | |||
|
539 | ||||
|
540 | def test_remove_ssh_key(self): | |||
|
541 | description = u'' | |||
|
542 | public_key = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost' | |||
|
543 | fingerprint = u'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8' | |||
|
544 | ||||
|
545 | self.log_user() | |||
|
546 | user = User.get_by_username(TEST_USER_REGULAR_LOGIN) | |||
|
547 | user_id = user.user_id | |||
|
548 | ||||
|
549 | response = self.app.post(url('edit_user_ssh_keys', id=user_id), | |||
|
550 | {'description': description, | |||
|
551 | 'public_key': public_key, | |||
|
552 | '_authentication_token': self.authentication_token()}) | |||
|
553 | self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint) | |||
|
554 | response.follow() | |||
|
555 | ssh_key = UserSshKeys.query().filter(UserSshKeys.user_id == user_id).one() | |||
|
556 | assert ssh_key.description == description | |||
|
557 | ||||
|
558 | response = self.app.post(url('edit_user_ssh_keys_delete', id=user_id), | |||
|
559 | {'del_public_key': ssh_key.public_key, | |||
|
560 | '_authentication_token': self.authentication_token()}) | |||
|
561 | self.checkSessionFlash(response, 'SSH key successfully deleted') | |||
|
562 | keys = UserSshKeys.query().all() | |||
|
563 | assert 0 == len(keys) | |||
|
564 | ||||
517 |
|
565 | |||
518 | class TestAdminUsersController_unittest(TestController): |
|
566 | class TestAdminUsersController_unittest(TestController): | |
519 | """ Unit tests for the users controller """ |
|
567 | """ Unit tests for the users controller """ |
@@ -66,6 +66,7 b" no_about.add(('Sean Farley <sean.michael" | |||||
66 | # history: |
|
66 | # history: | |
67 | other = [ |
|
67 | other = [ | |
68 | # Work folded into commits attributed to others: |
|
68 | # Work folded into commits attributed to others: | |
|
69 | ('2013', 'Ilya Beda <ir4y.ix@gmail.com>'), | |||
69 | ] |
|
70 | ] | |
70 |
|
71 | |||
71 | # Preserve contributors listed in about.html but not appearing in repository |
|
72 | # Preserve contributors listed in about.html but not appearing in repository |
General Comments 0
You need to be logged in to leave comments.
Login now