##// END OF EJS Templates
ssh-keys: added admin panel for managing globally all SSH Keys....
marcink -
r2042:e367c94a default
parent child Browse files
Show More
@@ -0,0 +1,91 b''
1
2 <div class="panel panel-default">
3 <div class="panel-heading">
4 <h3 class="panel-title">${_('SSH Keys')} - <span id="ssh_keys_count"></span></h3>
5
6 ${h.secure_form(h.route_path('admin_permissions_ssh_keys_update'), method='POST', request=request)}
7 <button class="btn btn-link pull-right" type="submit">${_('Update SSH keys file')}</button>
8 ${h.end_form()}
9 </div>
10 <div class="panel-body">
11 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12
13 <div id="repos_list_wrap">
14 <table id="ssh_keys_table" class="display"></table>
15 </div>
16 </div>
17 </div>
18
19
20 <script type="text/javascript">
21
22 $(document).ready(function() {
23 var $sshKeyListTable = $('#ssh_keys_table');
24
25 var getDatatableCount = function(){
26 var table = $sshKeyListTable.dataTable();
27 var page = table.api().page.info();
28 var active = page.recordsDisplay;
29 var total = page.recordsTotal;
30
31 var _text = _gettext("{0} out of {1} ssh keys").format(active, total);
32 $('#ssh_keys_count').text(_text);
33 };
34
35 // user list
36 $sshKeyListTable.DataTable({
37 processing: true,
38 serverSide: true,
39 ajax: "${h.route_path('admin_permissions_ssh_keys_data')}",
40 dom: 'rtp',
41 pageLength: ${c.visual.admin_grid_items},
42 order: [[ 0, "asc" ]],
43 columns: [
44 { data: {"_": "username",
45 "sort": "username"}, title: "${_('Username')}", className: "td-user" },
46 { data: {"_": "fingerprint",
47 "sort": "fingerprint"}, title: "${_('Fingerprint')}", className: "td-type" },
48 { data: {"_": "description",
49 "sort": "description"}, title: "${_('Description')}", className: "td-type" },
50 { data: {"_": "created_on",
51 "sort": "created_on"}, title: "${_('Created on')}", className: "td-time" },
52 { data: {"_": "action",
53 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
54 ],
55 language: {
56 paginate: DEFAULT_GRID_PAGINATION,
57 sProcessing: _gettext('loading...'),
58 emptyTable: _gettext("No ssh keys available yet.")
59 },
60
61 "createdRow": function ( row, data, index ) {
62 if (!data['active_raw']){
63 $(row).addClass('closed')
64 }
65 }
66 });
67
68 $sshKeyListTable.on('xhr.dt', function(e, settings, json, xhr){
69 $sshKeyListTable.css('opacity', 1);
70 });
71
72 $sshKeyListTable.on('preXhr.dt', function(e, settings, data){
73 $sshKeyListTable.css('opacity', 0.3);
74 });
75
76 // refresh counters on draw
77 $sshKeyListTable.on('draw.dt', function(){
78 getDatatableCount();
79 });
80
81 // filter
82 $('#q_filter').on('keyup',
83 $.debounce(250, function() {
84 $sshKeyListTable.DataTable().search(
85 $('#q_filter').val()
86 ).draw();
87 })
88 );
89
90 });
91 </script>
@@ -106,6 +106,16 b' def admin_routes(config):'
106 name='admin_permissions_auth_token_access',
106 name='admin_permissions_auth_token_access',
107 pattern='/permissions/auth_token_access')
107 pattern='/permissions/auth_token_access')
108
108
109 config.add_route(
110 name='admin_permissions_ssh_keys',
111 pattern='/permissions/ssh_keys')
112 config.add_route(
113 name='admin_permissions_ssh_keys_data',
114 pattern='/permissions/ssh_keys/data')
115 config.add_route(
116 name='admin_permissions_ssh_keys_update',
117 pattern='/permissions/ssh_keys/update')
118
109 # users admin
119 # users admin
110 config.add_route(
120 config.add_route(
111 name='users',
121 name='users',
@@ -20,7 +20,9 b''
20
20
21 import pytest
21 import pytest
22 from rhodecode.model.db import User, UserIpMap
22 from rhodecode.model.db import User, UserIpMap
23 from rhodecode.model.meta import Session
23 from rhodecode.model.permission import PermissionModel
24 from rhodecode.model.permission import PermissionModel
25 from rhodecode.model.ssh_key import SshKeyModel
24 from rhodecode.tests import (
26 from rhodecode.tests import (
25 TestController, clear_all_caches, assert_session_flash)
27 TestController, clear_all_caches, assert_session_flash)
26
28
@@ -55,7 +57,14 b' def route_path(name, params=None, **kwar'
55 'admin_permissions_ips':
57 'admin_permissions_ips':
56 ADMIN_PREFIX + '/permissions/ips',
58 ADMIN_PREFIX + '/permissions/ips',
57 'admin_permissions_overview':
59 'admin_permissions_overview':
58 ADMIN_PREFIX + '/permissions/overview'
60 ADMIN_PREFIX + '/permissions/overview',
61
62 'admin_permissions_ssh_keys':
63 ADMIN_PREFIX + '/permissions/ssh_keys',
64 'admin_permissions_ssh_keys_data':
65 ADMIN_PREFIX + '/permissions/ssh_keys/data',
66 'admin_permissions_ssh_keys_update':
67 ADMIN_PREFIX + '/permissions/ssh_keys/update'
59
68
60 }[name].format(**kwargs)
69 }[name].format(**kwargs)
61
70
@@ -248,3 +257,30 b' class TestAdminPermissionsController(Tes'
248 def test_index_overview(self):
257 def test_index_overview(self):
249 self.log_user()
258 self.log_user()
250 self.app.get(route_path('admin_permissions_overview'))
259 self.app.get(route_path('admin_permissions_overview'))
260
261 def test_ssh_keys(self):
262 self.log_user()
263 self.app.get(route_path('admin_permissions_ssh_keys'), status=200)
264
265 def test_ssh_keys_data(self, user_util, xhr_header):
266 self.log_user()
267 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
268 extra_environ=xhr_header)
269 assert response.json == {u'data': [], u'draw': None,
270 u'recordsFiltered': 0, u'recordsTotal': 0}
271
272 dummy_user = user_util.create_user()
273 SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key')
274 Session().commit()
275 response = self.app.get(route_path('admin_permissions_ssh_keys_data'),
276 extra_environ=xhr_header)
277 assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef'
278
279 def test_ssh_keys_update(self):
280 self.log_user()
281 response = self.app.post(
282 route_path('admin_permissions_ssh_keys_update'),
283 dict(csrf_token=self.csrf_token), status=302)
284
285 assert_session_flash(
286 response, 'SSH key support is disabled in .ini file')
@@ -21,6 +21,7 b''
21 import re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 import datetime
24 from pyramid.interfaces import IRoutesMapper
25 from pyramid.interfaces import IRoutesMapper
25
26
26 from pyramid.view import view_config
27 from pyramid.view import view_config
@@ -28,13 +29,15 b' from pyramid.httpexceptions import HTTPF'
28 from pyramid.renderers import render
29 from pyramid.renderers import render
29 from pyramid.response import Response
30 from pyramid.response import Response
30
31
31 from rhodecode.apps._base import BaseAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.events import trigger
32
35
33 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
34 from rhodecode.lib.auth import (
37 from rhodecode.lib.auth import (
35 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
38 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
36 from rhodecode.lib.utils2 import aslist
39 from rhodecode.lib.utils2 import aslist, safe_unicode
37 from rhodecode.model.db import User, UserIpMap
40 from rhodecode.model.db import or_, joinedload, coalesce, User, UserIpMap, UserSshKeys
38 from rhodecode.model.forms import (
41 from rhodecode.model.forms import (
39 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
42 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
40 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
@@ -45,7 +48,7 b' from rhodecode.model.settings import Set'
45 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
46
49
47
50
48 class AdminPermissionsView(BaseAppView):
51 class AdminPermissionsView(BaseAppView, DataGridAppView):
49 def load_default_context(self):
52 def load_default_context(self):
50 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
51
54
@@ -367,3 +370,106 b' class AdminPermissionsView(BaseAppView):'
367
370
368 c.whitelist_views = whitelist_views
371 c.whitelist_views = whitelist_views
369 return self._get_template_context(c)
372 return self._get_template_context(c)
373
374 @LoginRequired()
375 @HasPermissionAllDecorator('hg.admin')
376 @view_config(
377 route_name='admin_permissions_ssh_keys', request_method='GET',
378 renderer='rhodecode:templates/admin/permissions/permissions.mako')
379 def ssh_keys(self):
380 c = self.load_default_context()
381 c.active = 'ssh_keys'
382 return self._get_template_context(c)
383
384 @LoginRequired()
385 @HasPermissionAllDecorator('hg.admin')
386 @view_config(
387 route_name='admin_permissions_ssh_keys_data', request_method='GET',
388 renderer='json_ext', xhr=True)
389 def ssh_keys_data(self):
390 _ = self.request.translate
391 column_map = {
392 'fingerprint': 'ssh_key_fingerprint',
393 'username': User.username
394 }
395 draw, start, limit = self._extract_chunk(self.request)
396 search_q, order_by, order_dir = self._extract_ordering(
397 self.request, column_map=column_map)
398
399 ssh_keys_data_total_count = UserSshKeys.query()\
400 .count()
401
402 # json generate
403 base_q = UserSshKeys.query().join(UserSshKeys.user)
404
405 if search_q:
406 like_expression = u'%{}%'.format(safe_unicode(search_q))
407 base_q = base_q.filter(or_(
408 User.username.ilike(like_expression),
409 UserSshKeys.ssh_key_fingerprint.ilike(like_expression),
410 ))
411
412 users_data_total_filtered_count = base_q.count()
413
414 sort_col = self._get_order_col(order_by, UserSshKeys)
415 if sort_col:
416 if order_dir == 'asc':
417 # handle null values properly to order by NULL last
418 if order_by in ['created_on']:
419 sort_col = coalesce(sort_col, datetime.date.max)
420 sort_col = sort_col.asc()
421 else:
422 # handle null values properly to order by NULL last
423 if order_by in ['created_on']:
424 sort_col = coalesce(sort_col, datetime.date.min)
425 sort_col = sort_col.desc()
426
427 base_q = base_q.order_by(sort_col)
428 base_q = base_q.offset(start).limit(limit)
429
430 ssh_keys = base_q.all()
431
432 ssh_keys_data = []
433 for ssh_key in ssh_keys:
434 ssh_keys_data.append({
435 "username": h.gravatar_with_user(self.request, ssh_key.user.username),
436 "fingerprint": ssh_key.ssh_key_fingerprint,
437 "description": ssh_key.description,
438 "created_on": h.format_date(ssh_key.created_on),
439 "action": h.link_to(
440 _('Edit'), h.route_path('edit_user_ssh_keys',
441 user_id=ssh_key.user.user_id))
442 })
443
444 data = ({
445 'draw': draw,
446 'data': ssh_keys_data,
447 'recordsTotal': ssh_keys_data_total_count,
448 'recordsFiltered': users_data_total_filtered_count,
449 })
450
451 return data
452
453 @LoginRequired()
454 @HasPermissionAllDecorator('hg.admin')
455 @CSRFRequired()
456 @view_config(
457 route_name='admin_permissions_ssh_keys_update', request_method='POST',
458 renderer='rhodecode:templates/admin/permissions/permissions.mako')
459 def ssh_keys_update(self):
460 _ = self.request.translate
461 self.load_default_context()
462
463 ssh_enabled = self.request.registry.settings.get(
464 'ssh.generate_authorized_keyfile')
465 key_file = self.request.registry.settings.get(
466 'ssh.authorized_keys_file_path')
467 if ssh_enabled:
468 trigger(SshKeyFileChangeEvent(), self.request.registry)
469 h.flash(_('Updated SSH keys file: {}').format(key_file),
470 category='success')
471 else:
472 h.flash(_('SSH key support is disabled in .ini file'),
473 category='warning')
474
475 raise HTTPFound(h.route_path('admin_permissions_ssh_keys'))
@@ -56,6 +56,9 b' function registerRCRoutes() {'
56 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
56 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
57 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
57 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
58 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
58 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
59 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
60 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
61 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
59 pyroutes.register('users', '/_admin/users', []);
62 pyroutes.register('users', '/_admin/users', []);
60 pyroutes.register('users_data', '/_admin/users_data', []);
63 pyroutes.register('users_data', '/_admin/users_data', []);
61 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
64 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
@@ -44,6 +44,9 b''
44 <li class="${'active' if c.active=='auth_token_access' else ''}">
44 <li class="${'active' if c.active=='auth_token_access' else ''}">
45 <a href="${h.route_path('admin_permissions_auth_token_access')}">${_('AuthToken Access')}</a>
45 <a href="${h.route_path('admin_permissions_auth_token_access')}">${_('AuthToken Access')}</a>
46 </li>
46 </li>
47 <li class="${'active' if c.active=='ssh_keys' else ''}">
48 <a href="${h.route_path('admin_permissions_ssh_keys')}">${_('SSH Keys')}</a>
49 </li>
47 <li class="${'active' if c.active=='perms' else ''}">
50 <li class="${'active' if c.active=='perms' else ''}">
48 <a href="${h.route_path('admin_permissions_overview')}">${_('Overview')}</a>
51 <a href="${h.route_path('admin_permissions_overview')}">${_('Overview')}</a>
49 </li>
52 </li>
General Comments 0
You need to be logged in to leave comments. Login now