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 @@ -53,5 +53,16 @@ def includeme(config): name='admin_settings_sessions_cleanup', pattern=ADMIN_PREFIX + '/settings/sessions/cleanup') + # user auth tokens + config.add_route( + name='edit_user_auth_tokens', + pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens') + config.add_route( + name='edit_user_auth_tokens_add', + pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens/new') + config.add_route( + name='edit_user_auth_tokens_delete', + pattern=ADMIN_PREFIX + '/users/{user_id:\d+}/edit/auth_tokens/delete') + # Scan module for configuration decorators. config.scan() diff --git a/rhodecode/apps/admin/views/users.py b/rhodecode/apps/admin/views/users.py new file mode 100644 --- /dev/null +++ b/rhodecode/apps/admin/views/users.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2016-2017 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# RhodeCode Enterprise Edition, including its added features, Support services, +# and proprietary license terms, please see https://rhodecode.com/licenses/ + +import logging + +from pyramid.httpexceptions import HTTPFound +from pyramid.view import view_config + +from rhodecode.apps._base import BaseAppView +from rhodecode.lib.auth import ( + LoginRequired, HasPermissionAllDecorator, CSRFRequired) +from rhodecode.lib import helpers as h +from rhodecode.lib.utils import PartialRenderer +from rhodecode.lib.utils2 import safe_int +from rhodecode.model.auth_token import AuthTokenModel +from rhodecode.model.db import User +from rhodecode.model.meta import Session + +log = logging.getLogger(__name__) + + +class AdminUsersView(BaseAppView): + ALLOW_SCOPED_TOKENS = False + """ + This view has alternative version inside EE, if modified please take a look + in there as well. + """ + + def load_default_context(self): + c = self._get_local_tmpl_context() + c.auth_user = self.request.user + c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS + self._register_global_c(c) + return c + + def _redirect_for_default_user(self, username): + _ = self.request.translate + if username == User.DEFAULT_USER: + h.flash(_("You can't edit this user"), category='warning') + # TODO(marcink): redirect to 'users' admin panel once this + # is a pyramid view + raise HTTPFound('/') + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @view_config( + route_name='edit_user_auth_tokens', request_method='GET', + renderer='rhodecode:templates/admin/users/user_edit.mako') + def auth_tokens(self): + _ = self.request.translate + c = self.load_default_context() + + user_id = self.request.matchdict.get('user_id') + c.user = User.get_or_404(user_id, pyramid_exc=True) + self._redirect_for_default_user(c.user.username) + + c.active = 'auth_tokens' + + c.lifetime_values = [ + (str(-1), _('forever')), + (str(5), _('5 minutes')), + (str(60), _('1 hour')), + (str(60 * 24), _('1 day')), + (str(60 * 24 * 30), _('1 month')), + ] + c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] + c.role_values = [ + (x, AuthTokenModel.cls._get_role_name(x)) + for x in AuthTokenModel.cls.ROLES] + c.role_options = [(c.role_values, _("Role"))] + c.user_auth_tokens = AuthTokenModel().get_auth_tokens( + c.user.user_id, show_expired=True) + return self._get_template_context(c) + + def maybe_attach_token_scope(self, token): + # implemented in EE edition + pass + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @CSRFRequired() + @view_config( + route_name='edit_user_auth_tokens_add', request_method='POST') + def auth_tokens_add(self): + _ = self.request.translate + c = self.load_default_context() + + user_id = self.request.matchdict.get('user_id') + c.user = User.get_or_404(user_id, pyramid_exc=True) + self._redirect_for_default_user(c.user.username) + + lifetime = safe_int(self.request.POST.get('lifetime'), -1) + description = self.request.POST.get('description') + role = self.request.POST.get('role') + + token = AuthTokenModel().create( + c.user.user_id, description, lifetime, role) + self.maybe_attach_token_scope(token) + Session().commit() + + h.flash(_("Auth token successfully created"), category='success') + return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) + + @LoginRequired() + @HasPermissionAllDecorator('hg.admin') + @CSRFRequired() + @view_config( + route_name='edit_user_auth_tokens_delete', request_method='POST') + def auth_tokens_delete(self): + _ = self.request.translate + c = self.load_default_context() + + user_id = self.request.matchdict.get('user_id') + c.user = User.get_or_404(user_id, pyramid_exc=True) + self._redirect_for_default_user(c.user.username) + + del_auth_token = self.request.POST.get('del_auth_token') + + if del_auth_token: + AuthTokenModel().delete(del_auth_token, c.user.user_id) + Session().commit() + h.flash(_("Auth token successfully deleted"), category='success') + + return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py --- a/rhodecode/config/routing.py +++ b/rhodecode/config/routing.py @@ -315,13 +315,6 @@ def make_map(config): m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced', action='update_advanced', conditions={'method': ['PUT']}) - m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens', - action='edit_auth_tokens', conditions={'method': ['GET']}) - m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens', - action='add_auth_token', conditions={'method': ['PUT']}) - m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens', - action='delete_auth_token', conditions={'method': ['DELETE']}) - m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions', action='edit_global_perms', conditions={'method': ['GET']}) m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions', diff --git a/rhodecode/controllers/admin/users.py b/rhodecode/controllers/admin/users.py --- a/rhodecode/controllers/admin/users.py +++ b/rhodecode/controllers/admin/users.py @@ -452,70 +452,6 @@ class UsersController(BaseController): force_defaults=False) @HasPermissionAllDecorator('hg.admin') - def edit_auth_tokens(self, user_id): - user_id = safe_int(user_id) - c.user = User.get_or_404(user_id) - if c.user.username == User.DEFAULT_USER: - h.flash(_("You can't edit this user"), category='warning') - return redirect(url('users')) - - c.active = 'auth_tokens' - show_expired = True - c.lifetime_values = [ - (str(-1), _('forever')), - (str(5), _('5 minutes')), - (str(60), _('1 hour')), - (str(60 * 24), _('1 day')), - (str(60 * 24 * 30), _('1 month')), - ] - c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] - c.role_values = [(x, AuthTokenModel.cls._get_role_name(x)) - for x in AuthTokenModel.cls.ROLES] - c.role_options = [(c.role_values, _("Role"))] - c.user_auth_tokens = AuthTokenModel().get_auth_tokens( - c.user.user_id, show_expired=show_expired) - defaults = c.user.get_dict() - return htmlfill.render( - render('admin/users/user_edit.mako'), - defaults=defaults, - encoding="UTF-8", - force_defaults=False) - - @HasPermissionAllDecorator('hg.admin') - @auth.CSRFRequired() - def add_auth_token(self, user_id): - user_id = safe_int(user_id) - c.user = User.get_or_404(user_id) - if c.user.username == User.DEFAULT_USER: - h.flash(_("You can't edit this user"), category='warning') - return redirect(url('users')) - - lifetime = safe_int(request.POST.get('lifetime'), -1) - description = request.POST.get('description') - role = request.POST.get('role') - AuthTokenModel().create(c.user.user_id, description, lifetime, role) - Session().commit() - h.flash(_("Auth token successfully created"), category='success') - return redirect(url('edit_user_auth_tokens', user_id=c.user.user_id)) - - @HasPermissionAllDecorator('hg.admin') - @auth.CSRFRequired() - def delete_auth_token(self, user_id): - user_id = safe_int(user_id) - c.user = User.get_or_404(user_id) - if c.user.username == User.DEFAULT_USER: - h.flash(_("You can't edit this user"), category='warning') - return redirect(url('users')) - - del_auth_token = request.POST.get('del_auth_token') - if del_auth_token: - AuthTokenModel().delete(del_auth_token, c.user.user_id) - Session().commit() - h.flash(_("Auth token successfully deleted"), category='success') - - return redirect(url('edit_user_auth_tokens', user_id=c.user.user_id)) - - @HasPermissionAllDecorator('hg.admin') def edit_global_perms(self, user_id): user_id = safe_int(user_id) c.user = User.get_or_404(user_id) diff --git a/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako b/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako --- a/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako +++ b/rhodecode/templates/admin/my_account/my_account_auth_tokens.mako @@ -3,20 +3,21 @@

${_('Authentication Tokens')}

+

${_('Each token can have a role. Token with a role can be used only in given context, ' 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}

+ + + + + + + + %if c.user_auth_tokens: - - - - - - - - %for auth_token in c.user_auth_tokens: %endfor %else: - + %endif
${_('Token')}${_('Scope')}${_('Description')}${_('Role')}${_('Expiration')}${_('Action')}
${_('Token')}${_('Scope')}${_('Description')}${_('Role')}${_('Expiration')}${_('Action')}
@@ -52,9 +53,10 @@
${_('No additional auth token specified')}
${_('No additional auth tokens specified')}
+
${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')} @@ -66,7 +68,7 @@
- ${h.text('description', placeholder=_('Description'))} + ${h.text('description', class_='medium', placeholder=_('Description'))} ${h.select('lifetime', '', c.lifetime_options)} ${h.select('role', '', c.role_options)} @@ -76,9 +78,9 @@ ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')} % endif
-

- ${_('Repository scope works only with tokens with VCS type.')} -

+

+ ${_('Repository scope works only with tokens with VCS type.')} +

${h.submit('save',_('Add'),class_="btn")} diff --git a/rhodecode/templates/admin/users/user_edit.mako b/rhodecode/templates/admin/users/user_edit.mako --- a/rhodecode/templates/admin/users/user_edit.mako +++ b/rhodecode/templates/admin/users/user_edit.mako @@ -31,7 +31,7 @@