diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -106,6 +106,16 @@ def admin_routes(config): name='admin_permissions_auth_token_access', pattern='/permissions/auth_token_access') + config.add_route( + name='admin_permissions_ssh_keys', + pattern='/permissions/ssh_keys') + config.add_route( + name='admin_permissions_ssh_keys_data', + pattern='/permissions/ssh_keys/data') + config.add_route( + name='admin_permissions_ssh_keys_update', + pattern='/permissions/ssh_keys/update') + # users admin config.add_route( name='users', diff --git a/rhodecode/apps/admin/tests/test_admin_permissions.py b/rhodecode/apps/admin/tests/test_admin_permissions.py --- a/rhodecode/apps/admin/tests/test_admin_permissions.py +++ b/rhodecode/apps/admin/tests/test_admin_permissions.py @@ -20,7 +20,9 @@ import pytest from rhodecode.model.db import User, UserIpMap +from rhodecode.model.meta import Session from rhodecode.model.permission import PermissionModel +from rhodecode.model.ssh_key import SshKeyModel from rhodecode.tests import ( TestController, clear_all_caches, assert_session_flash) @@ -55,7 +57,14 @@ def route_path(name, params=None, **kwar 'admin_permissions_ips': ADMIN_PREFIX + '/permissions/ips', 'admin_permissions_overview': - ADMIN_PREFIX + '/permissions/overview' + ADMIN_PREFIX + '/permissions/overview', + + 'admin_permissions_ssh_keys': + ADMIN_PREFIX + '/permissions/ssh_keys', + 'admin_permissions_ssh_keys_data': + ADMIN_PREFIX + '/permissions/ssh_keys/data', + 'admin_permissions_ssh_keys_update': + ADMIN_PREFIX + '/permissions/ssh_keys/update' }[name].format(**kwargs) @@ -248,3 +257,30 @@ class TestAdminPermissionsController(Tes def test_index_overview(self): self.log_user() self.app.get(route_path('admin_permissions_overview')) + + def test_ssh_keys(self): + self.log_user() + self.app.get(route_path('admin_permissions_ssh_keys'), status=200) + + def test_ssh_keys_data(self, user_util, xhr_header): + self.log_user() + response = self.app.get(route_path('admin_permissions_ssh_keys_data'), + extra_environ=xhr_header) + assert response.json == {u'data': [], u'draw': None, + u'recordsFiltered': 0, u'recordsTotal': 0} + + dummy_user = user_util.create_user() + SshKeyModel().create(dummy_user, 'ab:cd:ef', 'KEYKEY', 'test_key') + Session().commit() + response = self.app.get(route_path('admin_permissions_ssh_keys_data'), + extra_environ=xhr_header) + assert response.json['data'][0]['fingerprint'] == 'ab:cd:ef' + + def test_ssh_keys_update(self): + self.log_user() + response = self.app.post( + route_path('admin_permissions_ssh_keys_update'), + dict(csrf_token=self.csrf_token), status=302) + + assert_session_flash( + response, 'SSH key support is disabled in .ini file') diff --git a/rhodecode/apps/admin/views/permissions.py b/rhodecode/apps/admin/views/permissions.py --- a/rhodecode/apps/admin/views/permissions.py +++ b/rhodecode/apps/admin/views/permissions.py @@ -21,6 +21,7 @@ import re import logging import formencode +import datetime from pyramid.interfaces import IRoutesMapper from pyramid.view import view_config @@ -28,13 +29,15 @@ from pyramid.httpexceptions import HTTPF from pyramid.renderers import render from pyramid.response import Response -from rhodecode.apps._base import BaseAppView +from rhodecode.apps._base import BaseAppView, DataGridAppView +from rhodecode.apps.ssh_support import SshKeyFileChangeEvent +from rhodecode.events import trigger from rhodecode.lib import helpers as h from rhodecode.lib.auth import ( LoginRequired, HasPermissionAllDecorator, CSRFRequired) -from rhodecode.lib.utils2 import aslist -from rhodecode.model.db import User, UserIpMap +from rhodecode.lib.utils2 import aslist, safe_unicode +from rhodecode.model.db import or_, joinedload, coalesce, User, UserIpMap, UserSshKeys from rhodecode.model.forms import ( ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm) from rhodecode.model.meta import Session @@ -45,7 +48,7 @@ from rhodecode.model.settings import Set log = logging.getLogger(__name__) -class AdminPermissionsView(BaseAppView): +class AdminPermissionsView(BaseAppView, DataGridAppView): def load_default_context(self): c = self._get_local_tmpl_context() @@ -367,3 +370,106 @@ class AdminPermissionsView(BaseAppView): c.whitelist_views = whitelist_views return self._get_template_context(c) + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='admin_permissions_ssh_keys', request_method='GET', + renderer='rhodecode:templates/admin/permissions/permissions.mako') + def ssh_keys(self): + c = self.load_default_context() + c.active = 'ssh_keys' + return self._get_template_context(c) + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='admin_permissions_ssh_keys_data', request_method='GET', + renderer='json_ext', xhr=True) + def ssh_keys_data(self): + _ = self.request.translate + column_map = { + 'fingerprint': 'ssh_key_fingerprint', + 'username': User.username + } + draw, start, limit = self._extract_chunk(self.request) + search_q, order_by, order_dir = self._extract_ordering( + self.request, column_map=column_map) + + ssh_keys_data_total_count = UserSshKeys.query()\ + .count() + + # json generate + base_q = UserSshKeys.query().join(UserSshKeys.user) + + if search_q: + like_expression = u'%{}%'.format(safe_unicode(search_q)) + base_q = base_q.filter(or_( + User.username.ilike(like_expression), + UserSshKeys.ssh_key_fingerprint.ilike(like_expression), + )) + + users_data_total_filtered_count = base_q.count() + + sort_col = self._get_order_col(order_by, UserSshKeys) + if sort_col: + if order_dir == 'asc': + # handle null values properly to order by NULL last + if order_by in ['created_on']: + sort_col = coalesce(sort_col, datetime.date.max) + sort_col = sort_col.asc() + else: + # handle null values properly to order by NULL last + if order_by in ['created_on']: + sort_col = coalesce(sort_col, datetime.date.min) + sort_col = sort_col.desc() + + base_q = base_q.order_by(sort_col) + base_q = base_q.offset(start).limit(limit) + + ssh_keys = base_q.all() + + ssh_keys_data = [] + for ssh_key in ssh_keys: + ssh_keys_data.append({ + "username": h.gravatar_with_user(self.request, ssh_key.user.username), + "fingerprint": ssh_key.ssh_key_fingerprint, + "description": ssh_key.description, + "created_on": h.format_date(ssh_key.created_on), + "action": h.link_to( + _('Edit'), h.route_path('edit_user_ssh_keys', + user_id=ssh_key.user.user_id)) + }) + + data = ({ + 'draw': draw, + 'data': ssh_keys_data, + 'recordsTotal': ssh_keys_data_total_count, + 'recordsFiltered': users_data_total_filtered_count, + }) + + return data + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @CSRFRequired() + @view_config( + route_name='admin_permissions_ssh_keys_update', request_method='POST', + renderer='rhodecode:templates/admin/permissions/permissions.mako') + def ssh_keys_update(self): + _ = self.request.translate + self.load_default_context() + + ssh_enabled = self.request.registry.settings.get( + 'ssh.generate_authorized_keyfile') + key_file = self.request.registry.settings.get( + 'ssh.authorized_keys_file_path') + if ssh_enabled: + trigger(SshKeyFileChangeEvent(), self.request.registry) + h.flash(_('Updated SSH keys file: {}').format(key_file), + category='success') + else: + h.flash(_('SSH key support is disabled in .ini file'), + category='warning') + + raise HTTPFound(h.route_path('admin_permissions_ssh_keys')) diff --git a/rhodecode/public/js/rhodecode/routes.js b/rhodecode/public/js/rhodecode/routes.js --- a/rhodecode/public/js/rhodecode/routes.js +++ b/rhodecode/public/js/rhodecode/routes.js @@ -56,6 +56,9 @@ function registerRCRoutes() { pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []); pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []); pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []); + pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []); + pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []); + pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []); pyroutes.register('users', '/_admin/users', []); pyroutes.register('users_data', '/_admin/users_data', []); pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']); diff --git a/rhodecode/templates/admin/permissions/permissions.mako b/rhodecode/templates/admin/permissions/permissions.mako --- a/rhodecode/templates/admin/permissions/permissions.mako +++ b/rhodecode/templates/admin/permissions/permissions.mako @@ -44,6 +44,9 @@
  • ${_('AuthToken Access')}
  • +
  • + ${_('SSH Keys')} +
  • ${_('Overview')}
  • diff --git a/rhodecode/templates/admin/permissions/permissions_ssh_keys.mako b/rhodecode/templates/admin/permissions/permissions_ssh_keys.mako new file mode 100644 --- /dev/null +++ b/rhodecode/templates/admin/permissions/permissions_ssh_keys.mako @@ -0,0 +1,91 @@ + +
    +
    +

    ${_('SSH Keys')} -

    + + ${h.secure_form(h.route_path('admin_permissions_ssh_keys_update'), method='POST', request=request)} + + ${h.end_form()} +
    +
    + + +
    +
    +
    +
    +
    + + +