Show More
@@ -302,6 +302,10 b' def admin_routes(config):' | |||
|
302 | 302 | pattern='/users/{user_id:\d+}/edit/auth_tokens', |
|
303 | 303 | user_route=True) |
|
304 | 304 | config.add_route( |
|
305 | name='edit_user_auth_tokens_view', | |
|
306 | pattern='/users/{user_id:\d+}/edit/auth_tokens/view', | |
|
307 | user_route=True) | |
|
308 | config.add_route( | |
|
305 | 309 | name='edit_user_auth_tokens_add', |
|
306 | 310 | pattern='/users/{user_id:\d+}/edit/auth_tokens/new', |
|
307 | 311 | user_route=True) |
@@ -143,7 +143,7 b' class TestAdminUsersView(TestController)' | |||
|
143 | 143 | response = self.app.get( |
|
144 | 144 | route_path('edit_user_auth_tokens', user_id=user_id)) |
|
145 | 145 | for token in auth_tokens: |
|
146 | response.mustcontain(token) | |
|
146 | response.mustcontain(token[:4]) | |
|
147 | 147 | response.mustcontain('never') |
|
148 | 148 | |
|
149 | 149 | @pytest.mark.parametrize("desc, lifetime", [ |
@@ -165,7 +165,7 b' class TestAdminUsersView(TestController)' | |||
|
165 | 165 | response = response.follow() |
|
166 | 166 | user = User.get(user_id) |
|
167 | 167 | for auth_token in user.auth_tokens: |
|
168 | response.mustcontain(auth_token) | |
|
168 | response.mustcontain(auth_token[:4]) | |
|
169 | 169 | |
|
170 | 170 | def test_delete_auth_token(self, user_util): |
|
171 | 171 | self.log_user() |
@@ -804,6 +804,25 b' class UsersView(UserAppView):' | |||
|
804 | 804 | c.role_vcs = AuthTokenModel.cls.ROLE_VCS |
|
805 | 805 | return self._get_template_context(c) |
|
806 | 806 | |
|
807 | @LoginRequired() | |
|
808 | @HasPermissionAllDecorator('hg.admin') | |
|
809 | @view_config( | |
|
810 | route_name='edit_user_auth_tokens_view', request_method='POST', | |
|
811 | renderer='json_ext', xhr=True) | |
|
812 | def auth_tokens_view(self): | |
|
813 | _ = self.request.translate | |
|
814 | c = self.load_default_context() | |
|
815 | c.user = self.db_user | |
|
816 | ||
|
817 | auth_token_id = self.request.POST.get('auth_token_id') | |
|
818 | ||
|
819 | if auth_token_id: | |
|
820 | token = UserApiKeys.get_or_404(auth_token_id) | |
|
821 | ||
|
822 | return { | |
|
823 | 'auth_token': token.api_key | |
|
824 | } | |
|
825 | ||
|
807 | 826 | def maybe_attach_token_scope(self, token): |
|
808 | 827 | # implemented in EE edition |
|
809 | 828 | pass |
@@ -50,6 +50,9 b' def includeme(config):' | |||
|
50 | 50 | name='my_account_auth_tokens', |
|
51 | 51 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens') |
|
52 | 52 | config.add_route( |
|
53 | name='my_account_auth_tokens_view', | |
|
54 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/view') | |
|
55 | config.add_route( | |
|
53 | 56 | name='my_account_auth_tokens_add', |
|
54 | 57 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new') |
|
55 | 58 | config.add_route( |
@@ -49,7 +49,7 b' class TestMyAccountAuthTokens(TestContro' | |||
|
49 | 49 | user = User.get(usr['user_id']) |
|
50 | 50 | response = self.app.get(route_path('my_account_auth_tokens')) |
|
51 | 51 | for token in user.auth_tokens: |
|
52 | response.mustcontain(token) | |
|
52 | response.mustcontain(token[:4]) | |
|
53 | 53 | response.mustcontain('never') |
|
54 | 54 | |
|
55 | 55 | def test_my_account_add_auth_tokens_wrong_csrf(self, user_util): |
@@ -79,7 +79,7 b' class TestMyAccountAuthTokens(TestContro' | |||
|
79 | 79 | response = response.follow() |
|
80 | 80 | user = User.get(user_id) |
|
81 | 81 | for auth_token in user.auth_tokens: |
|
82 | response.mustcontain(auth_token) | |
|
82 | response.mustcontain(auth_token[:4]) | |
|
83 | 83 | |
|
84 | 84 | def test_my_account_delete_auth_token(self, user_util): |
|
85 | 85 | user = user_util.create_user(password='qweqwe') |
@@ -25,7 +25,7 b' import string' | |||
|
25 | 25 | import formencode |
|
26 | 26 | import formencode.htmlfill |
|
27 | 27 | import peppercorn |
|
28 | from pyramid.httpexceptions import HTTPFound | |
|
28 | from pyramid.httpexceptions import HTTPFound, HTTPNotFound | |
|
29 | 29 | from pyramid.view import view_config |
|
30 | 30 | |
|
31 | 31 | from rhodecode.apps._base import BaseAppView, DataGridAppView |
@@ -164,6 +164,27 b' class MyAccountView(BaseAppView, DataGri' | |||
|
164 | 164 | c.role_vcs = AuthTokenModel.cls.ROLE_VCS |
|
165 | 165 | return self._get_template_context(c) |
|
166 | 166 | |
|
167 | @LoginRequired() | |
|
168 | @NotAnonymous() | |
|
169 | @CSRFRequired() | |
|
170 | @view_config( | |
|
171 | route_name='my_account_auth_tokens_view', request_method='POST', xhr=True, | |
|
172 | renderer='json_ext') | |
|
173 | def my_account_auth_tokens_view(self): | |
|
174 | _ = self.request.translate | |
|
175 | c = self.load_default_context() | |
|
176 | ||
|
177 | auth_token_id = self.request.POST.get('auth_token_id') | |
|
178 | ||
|
179 | if auth_token_id: | |
|
180 | token = UserApiKeys.get_or_404(auth_token_id) | |
|
181 | if token.user.user_id != c.user.user_id: | |
|
182 | raise HTTPNotFound() | |
|
183 | ||
|
184 | return { | |
|
185 | 'auth_token': token.api_key | |
|
186 | } | |
|
187 | ||
|
167 | 188 | def maybe_attach_token_scope(self, token): |
|
168 | 189 | # implemented in EE edition |
|
169 | 190 | pass |
@@ -352,7 +352,7 b' table.dataTable {' | |||
|
352 | 352 | margin-bottom: @padding; |
|
353 | 353 | |
|
354 | 354 | table.rctable td:first-child { |
|
355 |
width: |
|
|
355 | width: 120px; | |
|
356 | 356 | } |
|
357 | 357 | } |
|
358 | 358 |
@@ -111,6 +111,7 b' function registerRCRoutes() {' | |||
|
111 | 111 | pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']); |
|
112 | 112 | pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']); |
|
113 | 113 | pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']); |
|
114 | pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']); | |
|
114 | 115 | pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']); |
|
115 | 116 | pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']); |
|
116 | 117 | pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']); |
@@ -312,6 +313,7 b' function registerRCRoutes() {' | |||
|
312 | 313 | pyroutes.register('my_account_update', '/_admin/my_account/update', []); |
|
313 | 314 | pyroutes.register('my_account_password', '/_admin/my_account/password', []); |
|
314 | 315 | pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []); |
|
316 | pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []); | |
|
315 | 317 | pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []); |
|
316 | 318 | pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []); |
|
317 | 319 | pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []); |
@@ -47,3 +47,72 b' var UsersAutoComplete = function(input_i' | |||
|
47 | 47 | lookupFilter: autocompleteFilterResult |
|
48 | 48 | }); |
|
49 | 49 | }; |
|
50 | ||
|
51 | var _showAuthToken = function (authTokenId, showUrl) { | |
|
52 | ||
|
53 | Swal.fire({ | |
|
54 | title: _gettext('Show this authentication token?'), | |
|
55 | showCancelButton: true, | |
|
56 | confirmButtonColor: '#84a5d2', | |
|
57 | cancelButtonColor: '#e85e4d', | |
|
58 | showClass: { | |
|
59 | popup: 'swal2-noanimation', | |
|
60 | backdrop: 'swal2-noanimation' | |
|
61 | }, | |
|
62 | hideClass: { | |
|
63 | popup: '', | |
|
64 | backdrop: '' | |
|
65 | }, | |
|
66 | confirmButtonText: _gettext('Show'), | |
|
67 | showLoaderOnConfirm: true, | |
|
68 | allowOutsideClick: function () { | |
|
69 | !Swal.isLoading() | |
|
70 | }, | |
|
71 | preConfirm: function () { | |
|
72 | ||
|
73 | var postData = { | |
|
74 | 'auth_token_id': authTokenId, | |
|
75 | 'csrf_token': CSRF_TOKEN | |
|
76 | }; | |
|
77 | return new Promise(function (resolve, reject) { | |
|
78 | $.ajax({ | |
|
79 | type: 'POST', | |
|
80 | data: postData, | |
|
81 | url: showUrl, | |
|
82 | headers: {'X-PARTIAL-XHR': true} | |
|
83 | }) | |
|
84 | .done(function (data) { | |
|
85 | resolve(data); | |
|
86 | }) | |
|
87 | .fail(function (jqXHR, textStatus, errorThrown) { | |
|
88 | //reject("Failed to fetch Authentication Token") | |
|
89 | var message = formatErrorMessage(jqXHR, textStatus, errorThrown) | |
|
90 | Swal.showValidationMessage('Request failed: {0}'.format(message) | |
|
91 | ) | |
|
92 | }); | |
|
93 | }) | |
|
94 | } | |
|
95 | ||
|
96 | }) | |
|
97 | .then(function (result) { | |
|
98 | if (result.value) { | |
|
99 | var tmpl = ('<code>{0}</code>' + | |
|
100 | '<i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="{1}" title="Copy Token"></i>'); | |
|
101 | ||
|
102 | Swal.fire({ | |
|
103 | title: _gettext('Authentication Token'), | |
|
104 | html: tmpl.format(result.value.auth_token, result.value.auth_token), | |
|
105 | confirmButtonColor: '#84a5d2', | |
|
106 | cancelButtonColor: '#e85e4d', | |
|
107 | showClass: { | |
|
108 | popup: 'swal2-noanimation', | |
|
109 | backdrop: 'swal2-noanimation' | |
|
110 | }, | |
|
111 | hideClass: { | |
|
112 | popup: '', | |
|
113 | backdrop: '' | |
|
114 | }, | |
|
115 | }) | |
|
116 | } | |
|
117 | }) | |
|
118 | } No newline at end of file |
@@ -1,4 +1,10 b'' | |||
|
1 | 1 | <div class="panel panel-default"> |
|
2 | <script> | |
|
3 | var showAuthToken = function(authTokenId) { | |
|
4 | return _showAuthToken(authTokenId, pyroutes.url('my_account_auth_tokens_view')) | |
|
5 | } | |
|
6 | </script> | |
|
7 | ||
|
2 | 8 | <div class="panel-heading"> |
|
3 | 9 | <h3 class="panel-title">${_('Authentication Tokens')}</h3> |
|
4 | 10 | </div> |
@@ -21,9 +27,11 b'' | |||
|
21 | 27 | %if c.user_auth_tokens: |
|
22 | 28 | %for auth_token in c.user_auth_tokens: |
|
23 | 29 | <tr class="${('expired' if auth_token.expired else '')}"> |
|
24 |
<td class="t |
|
|
25 |
<div class="user_auth_tokens |
|
|
26 | <code>${auth_token.api_key}</code> | |
|
30 | <td class="td-authtoken"> | |
|
31 | <div class="user_auth_tokens"> | |
|
32 | <code class="cursor-pointer" onclick="showAuthToken(${auth_token.user_api_key_id})"> | |
|
33 | ${auth_token.token_obfuscated} | |
|
34 | </code> | |
|
27 | 35 | </div> |
|
28 | 36 | </td> |
|
29 | 37 | <td class="td-wrap">${auth_token.description}</td> |
@@ -44,7 +52,7 b'' | |||
|
44 | 52 | </td> |
|
45 | 53 | <td class="td-action"> |
|
46 | 54 | ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)} |
|
47 |
${ |
|
|
55 | <input name="del_auth_token" type="hidden" value="${auth_token.user_api_key_id}"> | |
|
48 | 56 | <button class="btn btn-link btn-danger" type="submit" |
|
49 | 57 | onclick="submitConfirm(event, this, _gettext('Confirm to delete this auth token'), _gettext('Delete'), '${auth_token.token_obfuscated}')" |
|
50 | 58 | > |
@@ -1,6 +1,12 b'' | |||
|
1 | 1 | <%namespace name="base" file="/base/base.mako"/> |
|
2 | 2 | |
|
3 | 3 | <div class="panel panel-default"> |
|
4 | <script> | |
|
5 | var showAuthToken = function(authTokenId) { | |
|
6 | return _showAuthToken(authTokenId, pyroutes.url('edit_user_auth_tokens_view', {'user_id': '${c.user.user_id}'})) | |
|
7 | } | |
|
8 | </script> | |
|
9 | ||
|
4 | 10 | <div class="panel-heading"> |
|
5 | 11 | <h3 class="panel-title"> |
|
6 | 12 | ${base.gravatar_with_user(c.user.username, 16, tooltip=False, _class='pull-left')} |
@@ -26,9 +32,11 b'' | |||
|
26 | 32 | %if c.user_auth_tokens: |
|
27 | 33 | %for auth_token in c.user_auth_tokens: |
|
28 | 34 | <tr class="${('expired' if auth_token.expired else '')}"> |
|
29 |
<td class="t |
|
|
30 |
<div class="user_auth_tokens |
|
|
31 |
<code |
|
|
35 | <td class="td-authtoken"> | |
|
36 | <div class="user_auth_tokens"> | |
|
37 | <code class="cursor-pointer" onclick="showAuthToken(${auth_token.user_api_key_id})"> | |
|
38 | ${auth_token.token_obfuscated} | |
|
39 | </code> | |
|
32 | 40 | </div> |
|
33 | 41 | </td> |
|
34 | 42 | <td class="td-wrap">${auth_token.description}</td> |
@@ -49,7 +57,7 b'' | |||
|
49 | 57 | </td> |
|
50 | 58 | <td class="td-action"> |
|
51 | 59 | ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), request=request)} |
|
52 |
${ |
|
|
60 | <input name="del_auth_token" type="hidden" value="${auth_token.user_api_key_id}"> | |
|
53 | 61 | <button class="btn btn-link btn-danger" type="submit" |
|
54 | 62 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');"> |
|
55 | 63 | ${_('Delete')} |
General Comments 0
You need to be logged in to leave comments.
Login now